| Developer: | Glenn's Plugins Like this plugin? Show your appreciation! |
| Category: | Energy Monitoring |
| Github: | Github Repo |
| Assistance: | Get help! |
| Plugin ID: | com.GlennNZ.indigoplugin.SolarSmart |
| Latest release: | v1.0.82 released on Nov. 6, 2025 |
| Release downloaded: | 5 times |
| Requires: | Indigo v2022.1.0 or higher |
| (Check the Releases tab below for older releases that may have different requirements) | |
| Download latest release |
![]()
Harness your surplus solar energy automatically. SolarSmart monitors your photovoltaic (PV) production, site consumption, battery power, and/or net grid flow to calculate “headroom” (excess power)[...]
The following improvements and fixes have landed since the last README update. These notes reflect version 1.0.70 (commit f7fdad16 on main).
Notes: - Device UI wording may still refer to “Quota Window (rolling)”; functionally, windows are aligned to 06:00 as described above. - Existing images and their locations (Images/…) are unchanged.
SolarSmart is an Indigo Domotics plugin that:

Headroom is the amount of excess power you can safely use right now.
Depending on your metering, SolarSmart can compute headroom from: - A single, accurate net grid meter (preferred when available). - Separate inputs for PV output, site consumption, and battery charge/discharge.
Sign conventions (typical, but verify your meters): - Net grid: - Negative watts = exporting to the grid (good; spare power). - Positive watts = importing from the grid (no spare power). - Battery: - Negative watts = discharging (adds to available power). - Positive watts = charging (consumes power).
1) Grid-Only Mode (preferred if supported) - Uses your net grid meter as the single source of truth. - Example: - Grid = −2424 W → exporting 2424 W - Headroom = 2424 W
2) PV + Consumption + Battery Mode - Used when a net grid meter state isn’t available. - Example: - PV = 3800 W - Site Consumption = 3134 W - Battery = −9000 W (discharging) - Headroom = PV − Consumption + Battery = 3800 − 3134 + 9000 = 9666 W
3) No Spare Power example - Grid-Only Mode - Grid = +1800 W → importing from grid - Headroom = −1800 W (loads should stop)

1) Install the plugin - Double-click SolarSmart.indigoPlugin. - Enable it in Indigo.
2) Create the Main Device - Choose your operating mode. - Select the meter/source states for PV, consumption, battery and/or net grid. - Set the scheduler check frequency.
3) Create one or more Load Devices - Assign each load a tier (1 = highest priority). - Choose how the load should be controlled (device on/off or action groups). - Optionally set runtime quotas and allowed time windows.
4) Watch it run - As headroom rises, SolarSmart starts loads beginning with Tier 1. - As headroom falls, SolarSmart sheds loads starting from the highest tier.
Key options typically include: - Mode: - Grid-Only - PV + Consumption + Battery - Meters / States: - Net Grid state (single number), and/or - PV watts, Site Consumption watts, Battery watts - Scheduler: - Check frequency (e.g., every 30–120 seconds) - Logging: - Info (default), Debug, Very Verbose
Recommendations: - Use Grid-Only Mode if your metering supports it (most accurate). - Start with a moderate check frequency (e.g., 60s). - Use Debug logging briefly while tuning.
Each load can be configured with: - Tier (priority): - Lower number = starts earlier and is shed later. - Control method: - Direct device on/off, or - Action Groups to start/stop. - Optional limits and constraints: - Quota (max runtime) per day/period. - Allowed time window (hh:mm → hh:mm). - Allowed days of week. - Optional minimum headroom to start, and/or sustained headroom to continue (if exposed in your version). - Safety off behavior when headroom drops below zero.
Note: Field names may vary slightly by version.

Note: - Preferred runtime windows are aligned to local 06:00: - 12h: 06:00–18:00 and 18:00–06:00 - 24h/1d: 06:00–06:00 next day - 2–3d: advance at 06:00 every N days
Some devices (pool pumps, chlorinators, ventilation fans, etc.) must run a minimum amount of time each quota window (e.g. per day or rolling window) regardless of how much excess solar was actually Scheduled Catch‑Up guarantees a fallback runtime:
Update (v1.0.70): - Catch‑up Window Period can be set independently of the Quota Period. Minutes accrue in a separate, 06:00‑aligned catch‑up slot (RuntimeCatchupMins). - For 2–3 day catch‑up periods, forced catch‑up starts only occur during the final 24 hours of the current catch‑up slot (and still inside the daily catch‑up time window).
| Term | Meaning |
|---|---|
| Catch‑up Runtime (mins) | The fallback minimum you want the load to achieve during the current catch‑up window. |
| Served (RuntimeCatchupMins) | Total minutes the load has already run in the current catch‑up window (any reason). |
| Remaining Fallback | catchupRuntimeMins − served (never below 0). |
| catchupActive | True only while the plugin forcibly runs the load to make up the deficit. |
| Concurrency | Obeys the “Max Concurrent Loads” setting on the Main device. |
| Scenario | Result |
|---|---|
| Fallback set to 120 min. Load already ran 155 min naturally. | No catch‑up needed (Remaining Fallback = 0). |
| Fallback = 180 min. Load ran only 70 min by window start. | Remaining Fallback = 110 → plugin forces ON in catch‑up window until +110 min accumulated. |
| Fallback = 60 min. Device has run 0 min. Window opens. | Plugin starts it (catchupActive = true). |
| While catch‑up running headroom goes negative. | Catch‑up continues (it ignores headroom) unless concurrency or you manually stop it. |
| Target reached (Remaining Fallback = 0) before window end. | Plugin stops the load; catchupActive = false. |
| Window closes with Remaining Fallback > 0. | Plugin stops the load; will try again next catch‑up window if still within the catch‑up period. |
solarsmartLoad).All must be true:
- enableCatchup is checked.
- catchupRuntimeMins > 0.
- Remaining Fallback > 0.
- Device is currently OFF.
- Current time is inside the defined catch‑up window.
- For multi‑day catch‑up periods (2d/3d): we are in the final 24h of the current catch‑up slot.
- Concurrency limit not exceeded.
- Only one catch‑up start per tick (internal safety throttle).
Catch‑up started device stops when: - Remaining Fallback = 0 (target satisfied), or - Current time exits the catch‑up window, or - For multi‑day catch‑up: not in the final slot‑day (enforced), or - You manually turn it OFF, or - The relevant window rolls over: - If using an independent catch‑up period, the catch‑up window boundary. - Otherwise, at quota window rollover (legacy behavior). - Plugin / Indigo restarts (it re‑evaluates next tick).
| State | Description |
|---|---|
catchupDailyTargetMins |
Exactly the configured fallback (Catch‑up Runtime). |
catchupRemainingTodayMins |
Minutes still needed to satisfy fallback (never negative), computed against the catch‑up window. |
catchupActive |
True only while plugin‑forced catch‑up run is in progress. |
catchupRunTodayMins |
Minutes accumulated under catch‑up active time only. |
catchupRunWindowAccumMins |
Mirrors catchupRunTodayMins (placeholder). |
catchupLastStart / catchupLastStop |
Time stamps (YYYY‑MM‑DD HH:MM:SS) when catch‑up run last began/ended. |
RuntimeCatchupMins |
Total runtime accrued in the current catch‑up window (any reason). |
CatchupAnchorTs |
Epoch marking the start of the current catch‑up slot (06:00‑aligned). |
RuntimeQuotaMins |
Preferred runtime accrued in the quota window (any reason). |
RemainingQuotaMins |
Remaining preferred (non‑fallback) runtime if a max is configured. |
catchupDailyTargetMinscatchupRemainingTodayMinscatchupActiveRuntimeCatchupMinsRuntimeQuotaMins| Check | Reason |
|---|---|
enableCatchup unchecked |
Feature disabled. |
catchupRuntimeMins = 0 |
No fallback required. |
| Remaining Fallback = 0 | Already satisfied by normal runtime. |
| Outside catch‑up window | Wait until window start. |
| For 2–3 day catch‑up | Not yet in the final day of the catch‑up slot. |
| Concurrency limit reached | Another load occupies a slot. |
| Start throttle | One catch‑up start already happened this tick. |
| Not enough time passed | Wait for the next scheduler interval. |
Turn on debug5 (in plugin preferences) to see lines like:
[CATCHUP][EVAL] LoadName tier=1 served=70m target=180m remaining=110m ...
[CATCHUP][START] LoadName: remaining=110m ...
[CATCHUP][KEEP] ...
[CATCHUP][STOP] ...
[CATCHUP][NO-NEED] ...
A summary line at tick end shows total candidates, starts, stops, and satisfied loads.
Estimate today’s and tomorrow’s PV generation so you can: - Pre‑run discretionary loads if tomorrow looks poor. - Time energy‑intensive tasks near the forecast peak. - Show production expectations on Indigo Control Pages.
forecast.solar with:| Direction | Degrees |
|---|---|
| South | 0 |
| West | 90 |
| North | 180 |
| East | 270 (you may also supply -90) |
| State | Description |
|---|---|
forecastTodayDate / forecastTomorrowDate |
Local date keys (YYYY‑MM‑DD). |
forecastTodayKWh / forecastTomorrowKWh |
Daily production estimate in kWh (stringified float). |
forecastPeakKWToday / forecastPeakKWTomorrow |
Peak instantaneous kW (float). |
forecastPeakTimeToday / forecastPeakTimeTomorrow |
Local timestamp of strongest production (YYYY‑MM‑DD HH:MM). |
forecastSolarSummary |
Comma‑separated daily totals (friendly format). |
forecastSolarPeaks |
“Peaks: = , …” summary. |
Preferences/Plugins/com.GlennNZ.indigoplugin.SmartSolar/forecast_main_<MainDeviceID>.json
(Contains the raw API payload including ratelimit info.)
| Behavior | Detail |
|---|---|
| Cache lifespan | 1 hour |
| Thread wake interval | 10 minutes |
| Network call frequency | At most once per hour (per Main device) |
| Rate limit exceeded | Reuse last cached payload; log INFO message. |
| Goal | Automation Idea |
|---|---|
| Run flexible load before poor day | If forecastTomorrowKWh < X then increase today’s run time. |
| Time EV / battery charge | Use forecastPeakTimeToday to schedule controlled ramp or preheat. |
| Control Page forecast panel | Display forecastSolarSummary & forecastSolarPeaks. |
| Symptom | Fix |
|---|---|
| No forecast states | Ensure checkbox enabled; verify server location; check log for errors. |
| Tomorrow values blank or 0 early | API sometimes populates later; will appear on next refresh. |
| Data stale > 1h | Delete cache file to force early refetch or wait for next hourly expiry. |
| Rate limit messages | Normal; plugin automatically falls back to cached data. |
| Label | Linked State |
|---|---|
| Today (kWh) | forecastTodayKWh |
| Peak (kW @ time) | forecastPeakKWToday + forecastPeakTimeToday |
| Tomorrow (kWh) | forecastTomorrowKWh |
| Tomorrow Peak | forecastPeakKWTomorrow + forecastPeakTimeTomorrow |
| Summary | forecastSolarSummary |
| Peaks | forecastSolarPeaks |
| Level | Content |
|---|---|
| INFO | Rate limit notices, start/stop summaries. |
| debug2 / debug3 | Cache hits/misses, normalization steps. |
| debug (general) | Summaries and peak lines, truncated raw JSON. |
| Task | Steps |
|---|---|
| Guarantee pump runs at least 2h overnight if solar was poor | Set Catch‑up Runtime = 120, window = 00:00–06:00, enable catch‑up. |
| Display today + tomorrow forecast on Control Page | Enable forecast, place forecastSolarSummary or individual states. |
| Diagnose why catch‑up not starting | Enable debug5, check [CATCHUP][EVAL] lines & remaining fallback. |
| Force fresh forecast fetch | Delete cache file or wait until >1h since last fetch. |
| Limit simultaneous forced runs | Adjust “Max Concurrent Loads” on Main device. |
Q: Does catch‑up exceed my regular max runtime quota? A: Catch‑up counts toward the same served minutes. It won’t ignore your preferred runtime cap: if quota is exhausted, the load is ineligible and catch‑up won’t start.
Q: Can a load be both normally running and catch‑up active? A: If it was already ON when deficit existed, it stays a “normal” run (catchupActive stays False). Catch‑up only marks ownership when it starts the load.
Q: Why is catchupRunTodayMins low even though fallback is satisfied?
A: Those minutes count only plugin‑forced (active) time. Normal passive runtime still reduces catchupRemainingTodayMins but does not increment the “run under catch‑up” counter.
Q: Can I change azimuth to use traditional compass values (e.g. 180 = South)? A: forecast.solar uses 0=South; the plugin applies your entry directly. Enter values per its convention (documented above).
The plugin logs to Indigo’s Event Log with selectable verbosity.
Typical messages:
Info - SolarSmart: Headroom = 2424 W (Grid −2424 W) - SolarSmart: Starting Tier 1 load “Pool Pump” - SolarSmart: Shedding Tier 3 load “Laundry Dryer” (headroom −450 W) - SolarSmart: Next scheduler check in 60s
Debug - SolarSmart: Mode=GridOnly, Grid=−2424 W, Headroom=2424 W - SolarSmart: Tier scan → T1:on, T2:eligible, T3:hold (window closed) - SolarSmart: Scheduled ‘start EV Charger’ (min window met) - SolarSmart: Quota remaining for “Water Heater”: 00:43:12 today
Very Verbose - SolarSmart: Tick(60s): PV=3800, Site=3134, Battery=−9000 → Headroom=9666 - SolarSmart: DOW allowed=True; TimeWindow 08:30–16:30 → within window
Diagnostics tips: - If event decisions look wrong, verify sign conventions for your meters. - If loads don’t start, check time windows/days, quotas, and tier ordering. - Use Debug temporarily to trace scheduling decisions end-to-end.
When you enable the (very verbose) debug7 flag in plugin preferences the scheduler emits a structured, multi‑section diagnostic block for each SmartSolar Load every tick. This is intended for adva[...]
Example (actual output):
══════════════════════════════════════════════════════════════════[...]
DBG7 Device: SolarSmart Load Pool 2000W No Heater (id=127719016) Tier 1
══════════════════════════════════════════════════════════════════[...]
Decision
Status: OFF Action: SKIP (quota) Skip: quota
Headroom Now: -111 W StartsThisTick: 0 RunningNow: 0
Preferred Window Runtime
Served (internal): 240 min Window Run (state): 240 min
Remaining (state): 0 min Remaining (shown row): 0 min
Used (state RuntimeQuotaMins): 240 min Percent: 100%
Anchor Start (ts): 1755149613.112328 (15:33:33)
Catch-up / Fallback
Active: state=False / mem=None Catch-up Str Col: Met
Target: 120 min Remaining: 0 min
Run Today (state): 0 min Run (active secs mem): 0 s
Window: 00:00 - 06:00 Enabled: True
External / Control
External Device On: False IsRunning(state): False
Start Ts: None (—) Cooldown Start: None (—)
Control Mode: device Cooldown Mins: 10
Power & Thresholds
Rated: 2000 W Needed (start threshold est): 2200 W
Surge Mult: 1.00 Start Margin %: 10 Keep Margin %: 5
Min Runtime: 30 min Max Runtime (per start run): 240 min Max Pref Window: 240 min
Quota Window Config: 1d
Concurrency Snapshot
Running Now: 0 Starts This Tick: 0
Raw Internal st[] Keys
IsRunning: False
catchup_run_secs: 0
quota_anchor_ts: 1755149613.112328
run_today_secs: 0.0
served_quota_mins: 240
start_ts: None
today_key: '2025-08-14'
──────────────────────────────────────────────────────────────────[...]
Decision
Preferred Window Runtime Tracks runtime within the rolling “preferred” quota window:
served_quota_mins).RuntimeWindowMins (resets at rollover; trimmed to Served on hydrate).RemainingQuotaMins after last update.(served / target) * 100 (clamped) → used to embed “(NN%)” into Status.Catch-up / Fallback Fallback (scheduled catch‑up) semantics:
catchupActive) and in‑memory (catchup_active) for validation.catchupDailyTargetMins (configured fallback minutes).catchupRemainingTodayMins (deficit after subtracting Served).catchup_run_secs).enableCatchup property.External / Control
None or False if action groups or not controllable.device or actionGroup.Power & Thresholds
ratedWatts.maxRuntimePerQuotaMins (target for the rolling preferred window).Concurrency Snapshot
Raw Internal st[] Keys
A dump of the plugin’s in‑memory dict for this load (sorted keys) to surface hidden values (e.g., cooldown_start, catchup_run_secs, quota_anchor_ts).
| Column | Meaning |
|---|---|
| Tier | Priority tier (1 = highest). |
| Load | Device name. |
| Rated W | Configured ratedWatts. |
| Status | ✓ RUN or ✗ OFF (emoji then word). |
| Time Run | RuntimeWindowMins (cosmetic current-window minutes). |
| Rem Mins | RemainingQuotaMins (preferred window allowance left). |
| Watts | Estimated Watts required to START (rated * surge * margin). |
| Catch-up | Off / Met / ACT Xm / Need Xm (see catch-up legend above). |
| Action | START / KEEP / STOP / SKIP (·) with icon. |
Status & Action Icons: - RUN / OFF: Running state indicator. - START: Started this tick. - KEEP: Stayed running. - STOP: Stopped this tick. - ·SKIP: Ineligible; reason encoded in parenthesis in debug lines (and Action cell uses SKIP).
| String | When Shown |
|---|---|
| Off | Catch‑up disabled or target <= 0. |
| Met | Target > 0 and deficit already satisfied (remaining = 0). |
| ACT Xm | Catch‑up logic started the device; X = remaining minutes to satisfy fallback. |
| Need Xm | Deficit exists but conditions (window/concurrency/etc.) not yet met for catch‑up start. |
In plugin preferences:
1. Set the overall log level high enough (e.g. DEBUG / 5).
2. Check the debug7 (or “Debug Level 7”) flag.
3. Save. The next scheduler tick will emit one DBG7 block per enabled SmartSolar Load device.
Disable debug7 after troubleshooting; it is intentionally verbose and can flood logs on short tick intervals.
| Symptom | DBG7 Clues | Likely Cause |
|---|---|---|
| Load never STARTS | Action always SKIP (quota/headroom/window) | Quota consumed; insufficient headroom; outside time/DOW window; cooldown not met; concurrency cap. |
| Load STARTS then STOPs quickly | STOP reason shows headroom low; run minutes < min runtime? | Headroom dipped below threshold AND min runtime already satisfied. Increase hysteresis or keep margin. |
| Catch-up never activates | Catch-up Str “Need Xm” but outside window or concurrency saturated | Ensure window times, concurrency, and remaining fallback > 0. |
| Catch-up does nothing though target set | Catch-up Str “Met” and Remaining=0 | Device already achieved minimum via normal runtime. |
| Percent stuck at 100% across restart | Anchor refreshed & Served == max; window not rolled yet | Normal if full allowance consumed; rolls over when horizon passes (or after manual adjustment). |
quota_anchor_ts marks when the current preferred window began.served_quota_mins → 0RemainingQuotaMins → full targetRuntimeWindowMins → 0If you have an example DBG7 block that feels ambiguous, open an issue including: - The exact block (copy/paste) - Expected vs observed behavior - Whether catch-up or quota changes happened recently
We can expand the legend or clarify messages further.
1) Pool Pump (Tier 1) - Goal: Run as much as possible on excess solar. - Config: - Tier 1, no quota or high quota (e.g., 6h/day). - Allowed window 09:00–17:00. - Behavior: - Starts early when headroom goes positive. - Sheds late when headroom tightens.
2) EV Charger (Tier 2 or 3) - Goal: Charge only when exporting, avoid imports. - Config: - Tier 2 or 3, set a daily quota (e.g., 2h). - Optional window 10:00–16:00. - Behavior: - Starts after Tier 1 loads are satisfied. - Stops quickly if headroom dips below zero.
3) Water Heater (Tier 2) - Goal: Heat during surplus; cap daily runtime. - Config: - Tier 2, quota 1h/day, window 11:00–15:00. - Behavior: - Opportunistic heating using sunshine, with strict cap.
| v1.0.82 | Requires Indigo v2022.1.0+ | Released Nov. 6, 2025 | Logic fixes, 100W buffers, Fix for Tier priority |
| Released on: | Nov. 6, 2025 |
| Requires: | Indigo v2022.1.0+ |
| Downloaded: | 5 times |
| Download this release | |
1.0.82
Add 100W buffer, don't stop loads unless headroom more than -100Watts
Add Main device override to stop less important tiers if more important tier now out of cooldown and able to run. (this issue was upsetting impression of events below)
1.0.77 Better Fix for Tiers starting and stopping
1.0.75 Fix tiers priority not always being followed
1.0.70 Move catchup to its own window period checked at the end and then runs
Fine tuning multiple day windows Catchup also is over the runtime window period - so no catchup unless in the final phase / last day of the window period
| Released on: | Sept. 1, 2025 |
| Requires: | Indigo v2022.1.0+ |
| Downloaded: | 3 times |
| Download this release | |
The following improvements and fixes have landed since the last README update. These notes reflect version 1.0.70 (commit f7fdad16 on main).
06:00‑aligned preferred runtime windows
Quota/Preferred runtime windows now align to local 06:00 for determinism (no rolling drift). 12h: 06:00–18:00 and 18:00–06:00; 24h/1d: 06:00–06:00 next day; 2–3 day windows start at 06:00 every N days. Counters reset exactly once at slot boundaries and survive restarts within a slot. Independent Catch‑up Window Period (optional)
New per‑load option: Catch‑up Window Period can be set independently of the Quota Period. Runtime now accrues into two windows in parallel: Preferred runtime window: mirrored in RuntimeQuotaMins, resets per quota period. Catch‑up window: mirrored in RuntimeCatchupMins, resets per catch‑up period. Catch‑up only forces starts inside the daily catch‑up time window (e.g., 00:00–06:00). For 2–3 day catch‑up periods, forcing is deferred until the final 24 hours of the catch‑up slot. New load states: RuntimeCatchupMins (minutes accumulated within the catch‑up window) CatchupAnchorTs (epoch at the start of the current catch‑up slot) Forecast.Solar client robustness
Local timezone handling hardened (DST‑aware); safe fixed‑offset fallback if IANA data unavailable. Local‑day aggregation and compact summaries; on‑disk cache (~1 hour) to respect rate limits. Grid‑only mode and Test Source
Grid‑only mode uses net grid power exclusively: Headroom = −GridPower (W). Test Source can override the Main device; when “Use Grid Data Only” is checked and Grid Power is provided, PV/Consumption/Battery entries are ignored and headroom derives from grid power only.