DEV Community

Cover image for Building an Autonomous Home Energy Manager with Python
Aniket Hingane
Aniket Hingane

Posted on

Building an Autonomous Home Energy Manager with Python

Title Animation

Subtitle: How I Built an Agent to Slash Electricity Bills by 40% Using Solar & Price Forecasting

TL;DR

I built an autonomous AI agent that manages a home battery system. By simulating real-time electricity prices (Time-of-Use) and solar generation, the agent decides when to charge, discharge, or hold energy. In my simulations, this optimize-first approach reduced electricity costs by over 40% compared to a standard "use-as-needed" strategy. This article breaks down the architecture, the Python implementation, and my lessons learned from this experiment.

Introduction

We are living in an era where energy prices are as volatile as stock markets. With the rise of renewable energy, utility companies are increasingly shifting to Time-of-Use (TOU) pricing models, where electricity is cheap when the sun shines or the wind blows, and expensive during evening peaks.

For a homeowner with solar panels and a battery, this presents a complex optimization problem. Do you use your solar energy now? Do you store it? Do you sell it back to the grid? Or do you charge your battery from the grill when prices are negative (yes, that happens!)?

I realized that a simple "if-else" timer isn't enough. I needed an agent—something that could look at the forecast, look at the current battery state, and make a decision to maximize savings.

So, I decided to build autonomous-energy-optimizer.

What's This Article About?

This article is a deep dive into my journey of building a Python-based energy management agent. I wanted to simulate a realistic environment where:

  1. Grid Prices fluctuate every hour.
  2. Solar Production follows the sun but is affected by random weather.
  3. Home Load varies based on morning and evening routines.
  4. An Agent sits in the middle, making high-stakes decisions to save money.

I will walk you through the architecture, the decision logic, and the code itself.

Tech Stack

For this experiment, I kept the stack improved but focused on Python's strengths in simulation and modeling:

  • Python 3.12: The core language.
  • Rich: For that beautiful, dashboard-like terminal output you saw in the header.
  • Dataclasses: For structured data modeling (Prices, State, Decisions).
  • Matplotlib/Mermaid: For visualizing the logic and results.

Why Read It?

If you are interested in:

  • AI Agents: Beyond just LLMs, seeing how rule-based and utility-based agents work in numerical environments.
  • Green Tech: Applying coding skills to sustainability problems.
  • Simulation: Learning how to model complex systems (weather, markets) in code.
  • Python Design Patterns: Seeing clean separation of concerns between Environment, Agent, and Simulation Controller.

Then this post is for you.

Let's Design

Before writing a single line of code, I grabbed my digital whiteboard to map out the system. I needed to visualize how the agent interacts with the grid and the home.

System Architecture

The system mimics a real-world setup. We have simulation models acting as the "Environment" (Grid, Sun, Home), and an Agent acting as the "Controller".

Architecture Diagram

The Logic Flow

The decision-making process is critical. Every hour, the simulation loop asks the agent: "Here is the state, what do we do?" The agent then evaluates the net load (Consumption - Solar) and the price signal to choose an action.

Flow Diagram

Sequence of Operations

Here is how a single tick (one hour) of the simulation executes:

Sequence Diagram

Let’s Get Cooking

Now, let's look at the code. I structured the project to be modular, making it easy to swap out the pricing model or the agent logic later.

1. Modeling the World (models.py)

First, I defined the data structures. I used Python's dataclass to keep things strict and clean. EnergyState tracks the battery and load, while Decision captures what the agent wants to do.

from dataclasses import dataclass
from typing import List, Optional
from datetime import datetime

@dataclass
class PriceData:
    timestamp: datetime
    grid_price: float  # Current grid price in $/kWh
    solar_production: float  # Current solar production in kW

@dataclass
class EnergyState:
    battery_level: float  # Current battery level in kWh
    battery_capacity: float  # Max battery capacity in kWh
    current_load: float  # Current house load in kW

    @property
    def battery_percentage(self) -> float:
        return (self.battery_level / self.battery_capacity) * 100

@dataclass
class Decision:
    action: str  # "CHARGE", "DISCHARGE", "HOLD"
    amount: float  # Amount of energy in kWh
    reason: str
Enter fullscreen mode Exit fullscreen mode

2. The Forecasting System (forecasting.py)

To make the simulation interesting, I needed dynamic data. PriceForecaster simulates a typical "Duck Curve" pricing model where electricity is expensive in the evening. SolarPredictor generates a bell curve for sun output, adding random cloud cover for realism.

class PriceForecaster:
    def __init__(self):
        # Base TOU pricing: Low (Night), High (Evening), Mid (Day)
        self.base_prices = {
            # ... (hourly breakdown) ...
            17: 0.45, 18: 0.45, 19: 0.40, # Peak Evening
            # ...
        }

    def get_price(self, timestamp: datetime) -> float:
        hour = timestamp.hour
        base = self.base_prices.get(hour, 0.15)
        # Add random volatility simulation
        volatility = random.uniform(-0.02, 0.05)
        return max(0.05, round(base + volatility, 3))
Enter fullscreen mode Exit fullscreen mode

3. The Brains: Energy Agent (agents.py)

This is where the magic happens. The agent looks at the PriceData and EnergyState.

If solar production exceeds household load (net_load < 0), it prioritizes charging the battery. Free energy!

If there is a deficit (we need power), it checks the price.

  • High Price (> discharge_threshold): Discharge the battery. Avoid paying the utility.
  • Low Price (< charge_threshold): Charge the battery from the grid, anticipating future high prices.
  • Normal Price: Just self-consume from the battery if available.
class EnergyAgent:
    def decide(self, state: EnergyState, market: PriceData) -> Decision:
        net_load = state.current_load - market.solar_production

        # Scenario A: Excess Solar
        if net_load < 0:
            excess = abs(net_load)
            if state.battery_level < state.battery_capacity:
                return Decision("CHARGE", excess, "Excess Solar -> Battery")
            else:
                return Decision("HOLD", 0, "Battery Full, Solar to Grid")

        # Scenario B: Solar Deficit (Need Energy)
        # High price time! Use battery if possible.
        if market.grid_price >= self.discharge_threshold:
            if state.battery_level > 0:
                amount = min(state.battery_level, net_load)
                return Decision("DISCHARGE", amount, "Peak Price -> Discharge Battery")

        # Low price time! Charge battery for later.
        if market.grid_price <= self.charge_threshold:
            if state.battery_level < state.battery_capacity:
                charge_amount = min(5.0, state.battery_capacity - state.battery_level)
                return Decision("CHARGE", charge_amount, f"Low Price (${market.grid_price}) -> Charge")

        return Decision("HOLD", 0, "Using Grid (Normal Operation)")
Enter fullscreen mode Exit fullscreen mode

4. The Simulation Loop (main.py)

Finally, I tied it all together with Rich to create that engaging terminal output. The loop steps through 24 hours, updates the simulated environment, asks the agent for a decision, calculates the costs, and updates the live table.

def main():
    # ... setup models ...

    with Live(table, refresh_per_second=4) as live:
        for _ in range(24): # Simulate 24 hours
            # 1. Update Environment
            market = PriceData(...)

            # 2. Agent Decision
            decision = agent.decide(state, market)

            # 3. Calculate Physics & Economics
            # ... (Cost calculation logic) ...

            # 4. Update UI
            table.add_row(...)
            time.sleep(0.15) # Cinematic effect
Enter fullscreen mode Exit fullscreen mode

Let's Setup

If you want to run this simulation yourself, here is how you can set it up in minutes.

  1. Clone the Repository:

    git clone https://github.com/aniket-work/autonomous-energy-optimizer
    cd autonomous-energy-optimizer
    
  2. Create Virtual Environment:

    python3 -m venv venv
    source venv/bin/activate
    
  3. Install Dependencies:

    pip install pandas numpy requests rich
    

Let's Run

Running the simulation is straightforward:

python main.py
Enter fullscreen mode Exit fullscreen mode

You will see the terminal come alive. The agent starts negotiating with the simulated grid.

In my test runs, the results were consistent:

  • Standard Cost (No Agent): ~$18.00 / day
  • AI Agent Cost: ~$10.00 / day
  • Savings: ~45%

The agent successfully dodged the "Peak Evening" prices (5 PM - 9 PM) by discharging the battery it charged cheaply during the sunny afternoon or the deep night.

Closing Thoughts

Building this Autonomous Home Energy Manager was a fantastic exercise in thinking about systems. It showed me that "Smart Homes" don't just need connectivity; they need intelligence.

A simple rule-based agent like the one I built can save significant money. Imagine what you could do with Reinforcement Learning (RL), predicting your specific household habits, or integrating with a real Tesla Powerwall API.

The future of energy is distributed and intelligent. And as developers, we have the tools to build that future.

Disclaimer

The views and opinions expressed here are solely my own and do not represent the views, positions, or opinions of my employer or any organization I am affiliated with. The content is based on my personal experience and experimentation and may be incomplete or incorrect. Any errors or misinterpretations are unintentional, and I apologize in advance if any statements are misunderstood or misrepresented.

Top comments (0)