Grid-Aware Energy Load Shifter
Shift heavy residential loads to the cheapest electricity hours using Home Assistant energy data.
Quick Start
# Find all energy-related entities in HA
python3 {baseDir}/scripts/ha_bridge.py discover
# Get a full energy dashboard snapshot (prices, solar, consumption, batteries)
python3 {baseDir}/scripts/ha_bridge.py energy-summary
# Turn on the EV charger
python3 {baseDir}/scripts/ha_bridge.py call-service switch/turn_on --entity-id switch.ev_charger
Connection
Two paths to reach Home Assistant:
- MCP (preferred): If the HA MCP server is configured, use
mcporter call homeassistant.<tool>directly. - REST API: Use
python3 {baseDir}/scripts/ha_bridge.py. RequiresHA_URLandHA_TOKENenvironment variables.
Security
Required credentials:
| Variable | Description |
|---|---|
HA_URL | Home Assistant base URL (e.g. http://homeassistant.local:8123) |
HA_TOKEN | Home Assistant Long-Lived Access Token |
Least-privilege recommendations:
- Create a dedicated Home Assistant user account for this skill (e.g.
openclaw-energy) - Generate a Long-Lived Access Token from that account only
- Limit the account's entity access to energy-related entities if your HA setup supports entity-level permissions
- Test with read-only commands first (
discover,energy-summary) before enabling device control
Domain allowlist: The call-service command restricts actions to energy-related domains only: switch, automation, script, climate, water_heater, input_boolean, input_number, number. All other domains (e.g. lock, alarm_control_panel) are blocked with exit code 2.
Commands
| Command | What it does | Example |
|---|---|---|
discover | List all energy entities | ha_bridge.py discover |
energy-summary | One-shot dashboard (prices + consumption + solar + storage) | ha_bridge.py energy-summary |
status <entity> | Read a single entity's state and attributes | ha_bridge.py status sensor.electricity_price |
call-service <d/s> | Call an energy-related HA service (restricted to allowed domains) | ha_bridge.py call-service switch/turn_on --entity-id switch.ev_charger |
history <entity> | Get state changes over last N hours | ha_bridge.py history sensor.grid_import --hours 24 |
All commands output JSON to stdout.
Load-Shifting Workflow
Follow these steps when asked about energy optimization:
- Discover available energy entities: run
discoverorenergy-summary - Read prices: Check pricing entities' state and attributes — look for:
- Hourly price arrays in
today/tomorrow/prices_today/ratesattributes price_levelattribute (CHEAP / NORMAL / EXPENSIVE)- Current vs. average price comparison
- Hourly price arrays in
- Identify deferrable loads: Find
switch.*entities for schedulable devices (EV charger, pool pump, dishwasher, washer/dryer, water heater) - Find the cheapest window: Scan hourly prices for the contiguous N-hour block with the lowest sum (N = estimated run time of device)
- Execute: Call
switch/turn_onat the optimal time, orautomation/triggerif the user has an existing automation
Interpreting Price Data
Different integrations expose prices differently:
- Hourly arrays (Nordpool, ENTSO-e, Octopus): Read
today/tomorrowattributes → find cheapest hours - Price level (Tibber): Read
price_level→ act when CHEAP or VERY_CHEAP - Real-time (Amber Electric): Read 5-minute pricing → shift loads immediately when cheap
- Utility meter tariffs: Read
sensor.*_peakvssensor.*_offpeak→ user's HA automations switch tariffs at configured times - Static TOU: Read
current_priceattribute → compare against historical average
Cost Savings Estimate
When recommending a shift, show estimated savings:
savings = (current_rate - cheapest_rate) × device_power_kw × run_duration_hours
Solar Self-Consumption
If solar sensors exist, align loads with peak production:
- Read
sensor.forecast_solar_*orsensor.solcast_*for today's forecast - Shift loads to hours with highest expected production
- This avoids grid import entirely — savings = full retail rate × kWh shifted
HVAC Pre-Conditioning
HVAC is the largest residential load (40-50% of electricity). Pre-cool or pre-heat during cheap/solar hours so the home coasts through expensive peak periods:
- Read
climate.*entities for current HVAC mode and setpoint - During cheapest window: lower cooling setpoint by 2-3F (pre-cool) or raise heating setpoint by 2-3F (pre-heat)
- During peak window: raise cooling setpoint by 2-3F to coast on thermal mass
- Savings estimate: 1.5-3 kW shifted × price differential × hours
Water Heater Scheduling
Electric water heaters (4.5 kW typical) are ideal deferrable loads:
- Find
switch.water_heaterorwater_heater.*entities - Heat during cheapest/solar window to full temperature
- Turn off during peak hours (tank maintains temperature for 4-6 hours)
- Savings estimate: 4.5 kW × price differential × 3-4 hours/day
Battery Arbitrage
If home battery entities exist (sensor.battery_soc, sensor.powerwall_*, sensor.enphase_*):
- Read current state of charge and charge/discharge rate limits
- Charge from grid during cheapest hours (or from solar)
- Discharge to home during peak price hours to avoid grid import
- Advanced: If battery supports grid export and VPP enrollment, discharge to grid during extreme price events ($2,000+/MWh)
- Savings estimate: battery_capacity_kwh × (peak_rate - valley_rate)
Demand Response / VPP Integration
For homes enrolled in utility demand response or virtual power plant programs:
- Read demand response signal entities (if available via HA integration)
- When DR event active: shed non-critical loads, pre-cool/pre-heat, discharge battery
- Estimate DR payment: kW reduced × event duration × program rate
Entity Reference
For detailed entity patterns across providers, read: energy_entities.md