DEV Community

The Refactored Road
The Refactored Road

Posted on • Originally published at refactoredroad.blogspot.com

Solar-Aware Appliance Scheduling with Homey — How I Built It

Since last time

A week ago I wrote about teaching my washing machine to pick its own schedule — one of my favorite smart home projects so far. At the end of that post I mentioned wanting cumulative savings tracking. That shipped in v1.3.0 about two days later — the device dashboard now shows per-schedule and lifetime savings.

A few other things landed between then and now:

  • v1.3.0 — Savings tracking, Frank Energie all-in pricing (spot + tax + markup + VAT), and a bunch of bug fixes including a UTC offset issue that served yesterday's prices during summer time.
  • v1.4.0 — Flow triggers for price changes and status changes, so you can build automations like "notify me when the price drops below X."
  • v1.5.0 — A "schedule cheapest start by time" action card. Instead of "within 6 hours," you say "before 14:00" and the scheduler figures out the window.
  • v1.5.1 — The rounding bug from the last post came back in disguise: low-power appliances like a phone charger showed EUR 0.00 savings because the rounding happened too early in the pipeline. Fixed by deferring all rounding to the display layer.

All useful, all driven by actually living with the app. But then I looked at my roof.

The solar problem

I have solar panels. When the sun is shining, I'm generating electricity that I either use or export to the grid. The feed-in tariff — what my energy company pays me for exported power — is about EUR 0.07/kWh. Meanwhile, buying from the grid costs EUR 0.15–0.35/kWh depending on the hour.

The old Power Profiler didn't know about any of this. It looked at grid prices and scheduled my washing machine at 3 AM because that's when electricity is cheapest. Technically correct. But if the sun is going to produce 3 kW of surplus at noon, running the washing machine then effectively costs me EUR 0.07/kWh (the feed-in I'm giving up) instead of EUR 0.25/kWh from the grid at 3 AM.

Simple fix, right? Just tell each device "solar hours are cheap." Except there's a catch.

I have a washing machine, a dryer, and a dishwasher. If all three independently decide that the noon slot is basically free, they'll all schedule into it. Their combined demand — maybe 4.5 kW — exceeds my 3 kW solar surplus, and the difference comes from the grid at full price. None of them accounted for the others. I call this the stampede problem.

Effective pricing

The solution is a centralized pricing service — a Virtual Energy Supplier — that computes an effective price per time slot. The effective price blends what your cheap sources (solar) and the grid contribute, weighted by how much capacity is actually available.

The math works like a waterfall:

  1. Take all cheap sources (PV panels, future: battery), sorted by cost
  2. Subtract base load — your fridge, router, standby devices eat into PV first
  3. Subtract capacity already reserved by other scheduled appliances
  4. Whatever cheap capacity is left goes to this appliance
  5. The rest comes from the grid

The blended price is then:

effectivePrice = (cheapKwh × cheapCost + gridKwh × gridPrice) / totalKwh
Enter fullscreen mode Exit fullscreen mode

For a zero-cost source like solar, the "cost" is the feed-in tariff — the opportunity cost of not exporting. This is economically correct: using your own solar isn't free, it's worth whatever you'd have earned by selling it.

One guard: the effective price never exceeds the grid price. If the math somehow produces a higher number (it shouldn't, but floating point), we cap it.

Knowing your base load

Here's something I didn't think about initially: not all solar production is available for scheduled appliances. Your house has a base load — the fridge cycling, the router, standby power, HVAC — that eats into PV output before anything else gets a turn.

My house draws about 200–400W overnight, 500–800W during the day. A fixed estimate of "500W base load" works okay, but it's wrong for every specific half-hour of the day.

If you have a smart meter connected to Homey (most Dutch homes have a P1 dongle), the app now automatically detects it and learns your actual base load pattern. The formula is simple:

baseLoad = gridPower + pvPower
Enter fullscreen mode Exit fullscreen mode

Where gridPower is your smart meter reading (positive = importing, negative = exporting) and pvPower is your inverter reading. This works regardless of whether you're importing or exporting. The app builds a 48-slot profile (one per 30 minutes) using an exponential moving average that adapts over about two weeks of observations.

No smart meter? The app falls back to a configurable constant (default 400W). Less accurate, but the system still works.

Solcast and correction factors

For solar forecasts, I integrated Solcast. Their Hobbyist plan gives you 10 API calls per day for free — enough for two forecast fetches and two actuals fetches, with budget to spare.

The forecasts are good, but they're not perfect for your specific installation. Maybe your panels face slightly north-west and get shaded by a tree after 15:00. Solcast doesn't know that.

So the app learns. For each 30-minute slot, it compares what Solcast predicted with what your inverter actually produced:

correctionFactor = actualYield / solcastEstimate
Enter fullscreen mode Exit fullscreen mode

This ratio is smoothed with an EMA (window of 14 observations, so roughly two weeks). After a few days, the app knows that Solcast overestimates your afternoon production by 15% and adjusts accordingly. The correction factor is capped at 3.0× to prevent one cloudy outlier from making the forecast permanently pessimistic.

One thing I got wrong initially: I recorded an API call against the daily budget before the fetch completed. If the request failed due to a network error, the call was wasted. Now it only counts on success — or on a 429 rate limit response, since that means the API actually processed it.

The stampede problem

Back to our three appliances all wanting the noon slot. The Virtual Energy Supplier solves this with a reservation ledger.

When prices or forecasts update, the app triggers a full replan:

  1. Release all existing capacity reservations
  2. Sort all scheduled devices by deadline (earliest first — highest priority)
  3. For each device, compute effective prices with the current reservations factored in, find the optimal window, and reserve that capacity

The key insight: device #2 sees device #1's reservation already deducted from the available surplus. If the noon slot only has 1.5 kW left after the washing machine reserved its share, the dryer computes a higher effective price for that slot and might pick a different time instead.

Sequential scheduling by deadline priority means the most urgent device gets first pick of the cheap slots. Less urgent devices adapt. No coordination protocol, no negotiation — just a deterministic ordering that produces good results.

The dashboard

All this math is invisible to the user if they don't care about it. The scheduler just picks better times. But if you do want to see what's happening, there's a new Virtual Energy Provider device — a dashboard that shows:

  • Current effective price vs. grid price
  • Feed-in tariff
  • PV surplus and available capacity
  • Household base load
  • Number of scheduled appliances

It also comes with flow cards. You can trigger automations when surplus exceeds a threshold ("turn on the EV charger when surplus > 2000W"), when the effective price drops below a value, or when the spot price goes negative (yes, that happens in the Netherlands — you get paid to consume).

What I learned

The biggest lesson: solar scheduling isn't a pricing problem — it's a resource allocation problem. Giving each device a "solar discount" is simple but wrong. You need a central arbiter that knows total supply, base demand, and existing commitments.

The second lesson: base load matters more than you'd think. Without accounting for the 600W my house draws at noon, I was overestimating available PV by about 20%. That's the difference between "washing machine runs on solar" and "washing machine runs on solar plus a bit of grid at full price."

Third: correction factors are essential. Weather-based forecasts for solar are impressively good at the regional level and consistently wrong for your specific roof. The EMA correction is cheap to compute and makes the scheduling decisions noticeably better after just a few days.

v1.6.0 is currently in the Test channel. If you have a Homey Pro with solar panels and want to try it, I'd love feedback. Next up: battery storage support and getting this through Homey's app certification.


Originally published on The Refactored Road.

Top comments (0)