A comprehensive scheduling component for ESPHome that integrates seamlessly with Home Assistant schedules. Control switches, buttons, and other devices based on time-based schedules with support for schedule variables (temperature, position, etc.) and multiple operating modes.
When paired with a RTC such as DS1307, this allows the device to operate independently from Home Assistant.
The Schedule component provides two platform types for different scheduling needs:
- Switch Platform - State-based scheduling for devices that need ON/OFF control over time periods (heating, lights, pumps)
- Button Platform - Event-based scheduling for momentary actions at specific times (blinds, feeders, notifications)
Both platforms share these core capabilities:
- ✅ Home Assistant Integration - Automatic sync with HA schedule helpers
- ✅ Persistent Storage - Schedules saved to NVS flash, survive reboots
- ✅ Schedule Variables - User-defined additional data (temperature, position) defined in Home Assistant schedule entries
- ✅ Multiple Modes - Manual and automatic operation modes
- ✅ Smart Mode Handling - All modes always visible, automatic validation and correction
- ✅ Error Notifications - Automatic HA notifications for invalid mode selections
- ✅ Offline Operation - Works with RTC when WiFi/HA unavailable
Both platforms require a time source. An RTC is recommended for offline operation:
time:
- platform: ds1307 # Or DS3231
id: rtc_time
update_interval: never
- platform: homeassistant
on_time_sync:
then:
ds1307.write_time:API connection required for schedule synchronization:
api:
encryption:
key: !secret api_key
homeassistant_services: trueBoth platforms include an update button to manually refresh the schedule from Home Assistant:
schedule_update_button:
name: "Update Schedule"Both platforms include a mode selector for manual override:
- Switch: 5 modes (Manual Off, Early Off, Auto, Manual On, Boost On)
- Button: 2 modes (Disabled, Enabled)
mode_selector:
name: "Schedule Mode"Both platforms support additional data attached to schedule entries, defined in Home Assistant and exposed as sensors in ESPHome:
scheduled_data_items:
- id: temp_sensor
label: "temp"
item_type: float
name: "Target Temperature"In Home Assistant, attach data to schedule entries:
{
"temp": 21.5
}Both platforms provide informational text sensors:
current_event:
name: "Current Event" # Shows current schedule state/event
next_event:
name: "Next Event" # Shows next upcoming event- ESP32 or derivatives - Tested on ESP32-S3. Storage requires NVS (ESP8266 not supported)
- Real-Time Clock (RTC) Module - Required for offline operation
- Supported: DS3231, DS1307, or compatible I2C RTC
- Maintains timekeeping during WiFi/HA outages
- ESPHome 2025.11.1 or later
- Home Assistant 2025.11.3 or later with schedule helper integration
- API connection to Home Assistant (for schedule sync)
- Development Board: Waveshare 6Ch Relay
- RTC: RTC Module Pico HAT
Choose your platform based on your use case:
- Switch Platform - For state-based scheduling (ON/OFF over time periods)
- Button Platform - For event-based scheduling (momentary actions at specific times)
switch:
- platform: schedule
id: heating_schedule
name: "Heating"
ha_schedule_entity_id: "schedule.heating"
schedule_update_button:
name: "Update Heating Schedule"
mode_selector:
name: "Heating Mode"
on_turn_on:
- logger.log: "Switch Turned ON by schedule"
on_turn_off:
- logger.log: "Switch Turned OFF by schedule" button:
- platform: schedule
id: feeder_schedule
name: "Feed Cat"
ha_schedule_entity_id: "schedule.cat_feeding"
schedule_update_button:
name: "Update Feeding Schedule"
mode_selector:
name: "Feeding Schedule Mode"
on_press:
- logger.log: "Feeding time!"Complete, ready-to-use YAML configuration examples are available in the examples/ folder:
- Switch Examples - Basic heating, thermostat control, multi-zone heating
- Button Examples - Cat feeder, blinds control, daily routines
- Examples README - Setup instructions and customization guide
For code snippets and detailed explanations, see Configuration Examples
For detailed technical information about component architecture, state machines, and internal workings:
- Technical Architecture - Component design and implementation details
- Adding New Components - Developer guide
The schedule component uses ESP32's NVS flash memory to persistently store schedules. The max_schedule_size setting pre-allocates NVS space for your schedules.
- Over-provisioning Warning: Setting
max_schedule_sizetoo high permanently reserves NVS space - NVS is Limited: ESP32 NVS partition is typically 12-20KB
- Right-sizing: Set
max_schedule_sizeto actual needs + small buffer (e.g., if you use 15 entries, set to 21, not 100) - Factory Reset Recovery: Reduce
max_schedule_sizeand factory reset to reclaim over-provisioned space
State-Based (Switch):
- Stores ON/OFF time pairs
- Storage: (entries × 4) + 4 bytes
- Example: 21 entries = 88 bytes
Event-Based (Button):
- Stores event times only
- Storage: (entries × 2) + 4 bytes (50% savings!)
- Example: 21 entries = 46 bytes
Create schedule helpers in Home Assistant:
Configuration → Helpers → Add Helper → Schedule
Add time slots for each day of the week. For schedule variables, see platform-specific documentation.
current_event(Optional, text_sensor config): Shows current schedule entry indexname(string): Sensor display name- Auto-generates ID:
{switch_id}_current_event_sensor
next_event(Optional, text_sensor config): Shows next schedule entry indexname(string): Sensor display name- Auto-generates ID:
{switch_id}_next_event_sensor
scheduled_data_items(Optional, list): Custom data fields for schedule entries- See Schedule Data Items below
- All other options from Switch Component are also available (e.g.,
icon,entity_category,disabled_by_default,on_turn_on,on_turn_off, etc.).
Add custom data to each schedule entry (temperature, brightness, etc.):
scheduled_data_items:
- id: target_temp
label: "temperature"
item_type: float
off_behavior: OFF_VALUE
off_value: 15.0
manual_behavior: MANUAL_VALUE
manual_value: 20.0-
id(Optional, ID): Component identifier. Auto-generated if not specified. Must be explicitly provided if you need to access the sensor value in lambdas or C++ code (e.g., usingSCHEDULE_GET_DATAmacro). -
label(Required, string): Field name in Home Assistant schedule data -
item_type(Required, enum): Data typeuint8_t- 0 to 255 (1 byte)uint16_t- 0 to 65,535 (2 bytes)int32_t- -2,147,483,648 to 2,147,483,647 (4 bytes)float- Floating point number (4 bytes)
Note: To minimize NVS storage usage, choose the smallest data type that accommodates your values. For example, use
uint8_tfor percentages (0-100) or temperatures in a limited range, rather thanfloatorint32_t. But the data item state returned will always be a float. -
off_behavior(Optional, enum): Behavior when schedule is OFF. Default:NANNAN- Sensor shows NaNLAST_ON_VALUE- Keep last ON valueOFF_VALUE- Use specificoff_value
-
off_value(Optional, number): Value to use whenoff_behaviorisOFF_VALUE -
manual_behavior(Optional, enum): Behavior in manual mode. Default:NANNAN- Sensor shows NaNLAST_ON_VALUE- Keep last ON valueMANUAL_VALUE- Use specificmanual_value
-
manual_value(Optional, number): Value to use whenmanual_behaviorisMANUAL_VALUE
The switch platform supports 5 operating modes via the mode selector:
| Mode | Behavior |
|---|---|
| Manual Off | Force switch OFF, ignore schedule completely |
| Early Off | Turn OFF immediately, return to Auto at next schedule event |
| Auto | Follow schedule automatically (default) |
| Manual On | Force switch ON, ignore schedule completely |
| Boost On | Turn ON immediately, return to Auto at next ON event |
Mode Availability:
- All 5 modes are always visible in Home Assistant (no integration reload required)
- Schedule-dependent modes (Auto, Early Off, Boost On) require a populated schedule
- If you select a schedule-dependent mode when the schedule is empty:
- A Home Assistant notification is sent explaining the issue
- The mode automatically reverts to Manual Off after 2 seconds
- If the schedule becomes empty while in a schedule-dependent mode:
- A notification is sent and the mode reverts to Manual Off
- Manual modes (Manual Off, Manual On) work regardless of schedule state
Access schedule components in lambda expressions:
// Check if schedule is currently ON
if (id(heating_schedule).state) {
// Schedule is ON
}
// Get current mode
auto mode = id(heating_schedule_mode_select).state;
// Get data sensor value using SCHEDULE_GET_DATA macro
float temp = SCHEDULE_GET_DATA(heating_schedule, "temperature");
// Check if valid value
if (!isnan(temp)) {
// Use temp value
}
// Change mode programmatically
auto call = id(heating_schedule_mode_select).make_call();
call.set_option("Auto");
call.perform();
// Trigger schedule update
id(heating_schedule_update_button).request_schedule();The SCHEDULE_GET_DATA macro provides a convenient way to retrieve data sensor values from a schedule component. It handles null pointer checking and logging automatically.
Important: If you need to access data sensor values in lambdas or other code, you must specify an id for each datasensor in your configuration.
Usage:
float value = SCHEDULE_GET_DATA(schedule_id, "label");Parameters:
schedule_id- The ID of your schedule component (withoutid()wrapper)label- The label string defined inscheduled_data_itemsconfiguration
Returns:
- The current sensor value as a float
NaNif the sensor is not found or hasn't been populated
Example:
// In your YAML configuration:
// scheduled_data_items:
// - id: heating_temp # ID is REQUIRED to access the value
// label: "temperature"
// item_type: float
// In lambda code:
float temp = SCHEDULE_GET_DATA(heating_schedule, "temperature");
if (!isnan(temp)) {
ESP_LOGI("heating", "Target temperature: %.1f�C", temp);
id(thermostat).target_temperature = temp;
} else {
ESP_LOGW("heating", "No temperature data available");
}Equivalent Long-Form Code:
// Instead of:
float temp = SCHEDULE_GET_DATA(heating_schedule, "temperature");
// You can write:
float temp = NAN;
auto *sensor = id(heating_schedule).get_data_sensor("temperature");
if (sensor != nullptr) {
temp = sensor->state;
} else {
ESP_LOGW("schedule", "Data sensor with label 'temperature' not found in schedule 'heating_schedule'");
}Advantages of Using the Macro:
- Less boilerplate code
- Automatic null pointer safety
- Consistent error logging
- Cleaner, more readable lambdas
- Reduces copy-paste errors
For detailed configuration examples including:
- Basic heating control
- Thermostat with temperature control
- Multi-zone heating systems
The Schedule component syncs with Home Assistant schedule helpers. This section explains how to create and configure HA schedules with additional data fields.
- Configuration → Helpers → Add Helper → Schedule
- Enter details:
- Name:
Heating(creates entityschedule.heating) - Icon:
mdi:radiator
- Name:
- Click Create
schedule:
heating:
name: Heating
icon: mdi:radiator
monday:
- from: "06:00:00"
to: "08:00:00"
- from: "17:00:00"
to: "22:00:00"
tuesday:
- from: "06:00:00"
to: "08:00:00"
- from: "17:00:00"
to: "22:00:00"
# ... continue for other daysEach schedule entry has:
- from: Start time (HH:MM:SS)
- to: End time (HH:MM:SS) - Only for state-based
- data: Custom data fields (optional)
schedule:
heating:
name: Heating Schedule
monday:
- from: "06:00:00"
to: "08:00:00"
data:
temperature: 21.0
- from: "17:00:00"
to: "22:00:00"
data:
temperature: 20.5
tuesday:
- from: "06:00:00"
to: "08:00:00"
data:
temperature: 21.0
- from: "17:00:00"
to: "22:00:00"
data:
temperature: 20.5
# ... continue for remaining daysschedule:
blinds:
name: Blinds Schedule
monday:
- from: "07:00:00"
to: "07:00:01"
data:
position: 100.0
- from: "20:00:00"
to: "20:00:01"
data:
position: 0.0
tuesday:
- from: "07:00:00"
to: "07:00:01"
data:
position: 100.0
- from: "20:00:00"
to: "20:00:01"
data:
position: 0.0
# ... continue for remaining daysNote: Event-based schedules require both from and to fields in Home Assistant, but only the from time triggers the event. Use to as from + 1 second.
Additional data fields allow you to store custom values with each schedule entry. These are accessed via data sensors in ESPHome.
Important: Data sensor IDs are auto-generated if not specified. However, if you need to access the sensor values in lambdas or other code (e.g., using SCHEDULE_GET_DATA), you must explicitly provide an id for each data sensor.
Home Assistant supports:
- Numbers: Integer or float values
- Strings: Text values (not currently supported by ESPHome component)
- Booleans: true/false (not currently supported by ESPHome component)
The label in ESPHome MUST match the field name in HA schedule data:
ESPHome:
scheduled_data_items:
- id: heating_temp
label: "temperature" # This must match HA
item_type: floatHome Assistant:
schedule:
heating:
monday:
- from: "06:00:00"
to: "08:00:00"
data:
temperature: 21.0 # Field name matches "temperature"You can have multiple data fields per entry:
ESPHome:
scheduled_data_items:
- id: target_temp
label: "temperature"
item_type: float
- id: target_humidity
label: "humidity"
item_type: uint8_t
- id: fan_speed
label: "fan_speed"
item_type: uint8_tHome Assistant:
schedule:
climate_control:
monday:
- from: "06:00:00"
to: "22:00:00"
data:
temperature: 21.5
humidity: 50
fan_speed: 2Data sensors show different values depending on schedule state and configuration:
| Schedule State | Mode | Data Sensor Value |
|---|---|---|
| ON | Auto | Current schedule data value |
| OFF | Auto | Depends on off_behavior |
| - | Manual Off/On | Depends on manual_behavior |
off_behavior Options:
NAN- Sensor showsNaNLAST_ON_VALUE- Keeps last ON valueOFF_VALUE- Shows configuredoff_value
manual_behavior Options:
NAN- Sensor showsNaNLAST_ON_VALUE- Keeps last ON valueMANUAL_VALUE- Shows configuredmanual_value
Data sensors always show the value from the next scheduled event. If no upcoming event has data, sensor shows NaN.
Home Assistant schedule helper:
schedule:
multi_zone_heating:
name: Multi-Zone Heating
icon: mdi:radiator
monday:
- from: "06:00:00"
to: "08:00:00"
data:
living_room_temp: 21.0
bedroom_temp: 18.0
bathroom_temp: 22.0
- from: "17:00:00"
to: "22:00:00"
data:
living_room_temp: 20.5
bedroom_temp: 19.0
bathroom_temp: 21.0
tuesday:
- from: "06:00:00"
to: "08:00:00"
data:
living_room_temp: 21.0
bedroom_temp: 18.0
bathroom_temp: 22.0
- from: "17:00:00"
to: "22:00:00"
data:
living_room_temp: 20.5
bedroom_temp: 19.0
bathroom_temp: 21.0
# ... continue for remaining daysESPHome configuration:
switch:
- platform: schedule
id: heating_schedule
name: "Heating Schedule"
ha_schedule_entity_id: "schedule.multi_zone_heating"
schedule_update_button:
name: "Update Heating Schedule"
mode_selector:
name: "Heating Mode"
scheduled_data_items:
- id: living_room_target
label: "living_room_temp"
item_type: float
off_behavior: OFF_VALUE
off_value: 15.0
- id: bedroom_target
label: "bedroom_temp"
item_type: float
off_behavior: OFF_VALUE
off_value: 15.0
- id: bathroom_target
label: "bathroom_temp"
item_type: float
off_behavior: OFF_VALUE
off_value: 16.0
sensor:
- platform: template
name: "Living Room Target"
lambda: 'return id(living_room_target).state;'
unit_of_measurement: "°C"
accuracy_decimals: 1
- platform: template
name: "Bedroom Target"
lambda: 'return id(bedroom_target).state;'
unit_of_measurement: "°C"
accuracy_decimals: 1
- platform: template
name: "Bathroom Target"
lambda: 'return id(bathroom_target).state;'
unit_of_measurement: "°C"
accuracy_decimals: 1Symptoms: ESPHome doesn't receive schedule updates
Checks:
- Verify
ha_schedule_entity_idmatches exactly - Check ESPHome logs for API errors
- Ensure Home Assistant API connection is working
- Press the update button manually to force sync
Symptoms: Data sensors always show NaN
Checks:
- Verify
labelin ESPHome matches field name in HA exactly - Check
off_behaviorandmanual_behaviorsettings - Ensure schedule entries actually have the data field
- Check ESPHome logs for parsing errors
Symptoms: Error notification about incorrect data type
Checks:
- Verify
item_typematches data type in HA - Use
floatfor decimal numbers - Use
int32_tfor whole numbers (positive or negative) - Use
uint8_toruint16_tfor small positive integers
Symptoms: Error notification about oversized schedule
Solution:
- Reduce number of schedule entries in HA
- Increase
max_schedule_sizein ESPHome (default: 21) - Consider splitting into multiple schedules
- Consistent Naming: Use clear, descriptive names for schedules and data fields
- Field Name Matching: Always ensure ESPHome
labelmatches HA field name exactly - Data Types: Use appropriate data types to save storage
- Entry Limits: Keep entries under limit (default 21) or adjust
max_schedule_size - Manual Updates: Press update button after modifying HA schedule
- Validation: Check ESPHome logs and HA notifications for errors
Choose the right platform for storage efficiency:
State-Based (Switch):
- Each entry: 4 bytes (2 bytes ON time + 2 bytes OFF time)
- Overhead: 4 bytes (entry count + terminator)
- Example: 21 entries = (21 × 4) + 4 = 88 bytes
Event-Based (Button):
- Each entry: 2 bytes (event time only)
- Overhead: 4 bytes (entry count + terminator)
- Example: 21 entries = (21 × 2) + 4 = 46 bytes (47% savings!)
NVS Space Management:
The max_schedule_size setting pre-allocates NVS space. To calculate total NVS usage:
State-Based: (max_schedule_size × 4) + 4 bytes per schedule
Event-Based: (max_schedule_size × 2) + 4 bytes per schedule
Example - Multiple Schedules:
- 3 switches with max_schedule_size=21: 3 × 88 = 264 bytes
- 2 buttons with max_schedule_size=21: 2 × 46 = 92 bytes
- Total NVS: 356 bytes
- Setup Time: ~100-200ms per component
- Loop Cycle: ~20ms (state machine check)
- NVS Writes: Only on schedule updates (not on every state change)
- Memory Usage: ~200 bytes + schedule data + data sensors
The component automatically sends Home Assistant persistent notifications for:
- Schedule Retrieval Failure - HA entity not found
- Parsing Errors - Invalid JSON or structure
- Missing Fields - Required data missing
- Invalid Values - Out of range or wrong type
- Oversized Schedule - Too many entries
- Data Sensor Errors - Missing or incorrect data type
Notifications include:
- ESPHome device name
- Schedule entity ID
- Specific error details
- Timestamp
Schedules are stored in ESP32 NVS (Non-Volatile Storage) flash and persist across:
- Reboots
- Power cycles
- Firmware updates (if NVS partition preserved)
To backup:
- Schedules sync from Home Assistant (no manual backup needed)
To restore:
- Press update button after restoring HA
- Or wait for automatic sync on next update
Factory Reset (NVS Recovery):
If you've over-provisioned max_schedule_size during development, you can reclaim NVS space:
- Reduce
max_schedule_sizein your YAML to actual needs - Perform a factory reset (clears all NVS data)
- Recompile and upload firmware
- Re-download schedules from Home Assistant
This is useful when transitioning from development (with large safety margins) to production (optimized storage).
- Ready-to-Use Examples:
examples/folder - Complete YAML configurations - Full Documentation:
docs/folder - Configuration Examples:
docs/EXAMPLES.md- Code snippets and explanations - Architecture Details:
docs/ARCHITECTURE.md - Developer Guide:
docs/ADDING_NEW_PLATFORMS.md - Quick Reference:
docs/QUICK_REFERENCE.md
Component Version: 1.0.0
ESPHome Version: 2024.6.0+
Home Assistant Version: 2024.6.0+
Repository: pebblebed-tech/schedule