Repository navigation
Optimization Technical Reference
This reference explains planning and execution internals. For normal setup, use Optimization settings and Smart Optimization. Prices, a proposed schedule, an effective action, a device command and measured physical power are separate evidence levels.
When using dynamic pricing (Amber, Localvolts, AEMO, Octopus Agile/Flux, Flow Power, EPEX), the optimizer receives price forecasts up to 48 hours ahead. Near-term prices are accurate but far-future forecasts are speculative — a predicted 40c/kWh spike at 2am tomorrow might settle at 22c.
Without adjustment, the optimizer might charge overnight at 20c for a "spike" 18 hours away that never materialises, when cheaper midday solar charging is available in between.
Confidence decay pulls far-future above-median prices toward the median as they get further from now:
| Parameter | Value | Description |
|---|---|---|
| Horizon | 6 hours; 12 hours with Profit Max | Prices within the horizon are unchanged; above-median prices beyond it are discounted |
| Decay rate | 0.15 | How quickly speculative prices are discounted beyond the horizon |
With a median of 21c/kWh and a raw forecast price of 30c/kWh:
| Hours ahead | Decayed price |
|---|---|
| 7h (just outside horizon) | 28.8c |
| 10h | 25.9c |
| 14h | 23.7c |
| 24h | 21.6c (nearly at average) |
The decay is asymmetric — only above-median prices are decayed. Below-median (cheap) prices are preserved because cheap periods are structurally reliable: midday solar dumps and off-peak overnight rates are predictable, not speculative.
This keeps midday at 15c cheaper than overnight at 18c in the price model. It discounts distant spikes rather than removing them: a very large forecast can still influence charging and holds. Solar availability, forecast load, reserve constraints and Charge By Time also affect the result. The table above uses the normal 6-hour horizon; Profit Max starts decay after 12 hours.
Note: Confidence decay is not applied to static TOU providers (GloBird, custom tariffs) where prices are known and fixed.
Amber users can enable Amber Forecast Confidence under Battery & Forecast Inputs. It discounts the premium above an ordinary-price band from the first future interval, using a provisional 90-minute half-life. It is off by default and replaces Amber export decay only when usable source metadata and ordinary-price samples are available. Import decay remains unchanged. Profit Max uses the same new weighting when this option is enabled.
Displayed and settlement prices remain intact. The setting can favour export sooner, but can also miss a higher later price and does not guarantee a partial hedge. Turning it off restores the existing six-/twelve-hour export-decay policy.
This section describes how the Smart Optimizer works internally. It is intended for users who want to understand why the LP chose a particular action, why it may hold charge instead of exporting, or why the battery returned to self-consumption at the optimizer reserve.
PowerSync's optimizer is a rolling linear program. Each solve builds a fresh forecast from the current Home Assistant state, calculates a 48-hour schedule at 5-minute resolution, stores the schedule for the dashboard and mobile app, then immediately executes the current slot.
The optimizer normally re-runs on aligned 5-minute wall-clock boundaries. It can also be triggered by fresh price data or coordinator refreshes. A lock prevents overlapping solves, so if multiple triggers arrive at the same boundary only one LP run is allowed to proceed.
The schedule is not a static upload to the battery. PowerSync keeps recalculating, then applies the current action through the active battery controller. This lets the plan react to updated prices, updated solar forecasts, changed SOC, EV charging plans, manual force modes, curtailment state, and Home Assistant restarts.
Each solve uses these main inputs:
| Input | How It Is Used |
|---|---|
| Import prices | Cost of drawing from the grid for each 5-minute slot |
| Export prices | Value, or cost, of exporting to the grid for each slot |
| Daily supply charge | Fixed retailer cost included in the local-day Cost Neutral target when that goal is enabled |
| Solar forecast | Expected PV generation in kW from Solcast, Open-Meteo Solar Forecast, or Volcast |
| Load forecast | Expected home load in kW, learned from Home Assistant recorder history |
| Current SOC | Starting battery state of charge |
| Battery capacity | Converts charge/discharge power into SOC movement |
| Maximum charge/discharge power | Caps the LP's battery power decisions |
| Maximum grid import | Caps total planned site grid import after forecast load and solar are accounted for |
| Optimizer backup reserve | Software SOC floor used by the LP and execution guard |
| Hardware backup reserve | Optional physical reserve written to supported batteries as a safety net |
| Grid-charge setting | Controls whether grid import may charge the battery |
| Export permission slots | Controls where intentional battery-to-grid export is allowed |
| EV plan | Adds planned EV charging to the load forecast so the battery and EV do not fight each other |
| Demand charge state | Adds import penalties and can override actions during demand windows |
Load forecasting uses a 30-day recorder-history model grouped by local weekday and time bucket. Recorder samples are recency-weighted, outlier-clipped, and blended with same weekday/weekend history when an exact bucket is sparse. Away Mode excludes the away interval from that history window so holiday usage does not become the normal baseline.
Solar forecasting uses the selected Solcast, Open-Meteo Solar Forecast, or Volcast source where available. If no supported solar forecast source is configured, stale, or unavailable, the optimizer has little or no visibility of future solar and the schedule becomes mostly price-driven.
The prices shown in the app are the display prices. The LP may use adjusted prices internally so it can model tariff features and operational protections:
- Dynamic provider forecasts are converted to 5-minute import/export arrays.
- Static TOU tariffs are expanded into 5-minute forecast slots.
- Flow Power Happy Hour is applied as a fixed export window where configured.
- Export Boost, Octopus Saving Sessions, Chip Mode, and demand-charge penalties are overlaid before the LP solve.
- For dynamic tariffs, confidence decay pulls far-future above-median prices toward the median. Cheap prices are preserved because cheap periods are usually structural, while distant spikes are less certain.
- Flow Power is not confidence-decayed because Happy Hour is contractual rather than speculative.
This means the chart can show the raw tariff while the LP uses a more conservative or feature-aware price signal.
When SciPy is available, PowerSync calls scipy.optimize.linprog using four variables for every time step:
| Variable | Meaning |
|---|---|
grid_import[t] |
Grid power imported in kW |
grid_export[t] |
Grid power exported in kW |
battery_charge[t] |
Battery charging power in kW |
battery_discharge[t] |
Battery discharging power in kW |
The objective is cost minimisation:
minimise sum(import_price[t] * grid_import[t] - export_price[t] * grid_export[t]) * interval_hours
PowerSync then adds a few practical terms:
- A terminal SOC value so the LP does not treat the final slot as a reason to empty the battery for free.
- A free-electricity charge bonus so 0c import windows are used decisively.
- Small tie-breaker values so equal-price periods form stable contiguous blocks instead of scattered one-slot actions.
- A Profit Max terminal weight reduction when Profit Max is enabled, making future stored energy less valuable relative to near-term export revenue.
When equal-price smoothing bridges a single self-consumption slot inside a planned export, target-power batteries keep the active export command for its validated 20-minute commitment instead of dropping to the bridge slot's lower natural-discharge target. Export eligibility and price, EV preservation, calibration, demand protection, projected reserve, site and network limits, and manual or external ownership remain immediate safety gates.
The LP does not simply compare the buy price and sell price directly. It models battery energy losses and the cost or opportunity cost of replacing stored energy later.
PowerSync starts with a conservative default battery efficiency of 92% in
each direction:
stored_energy = charge_energy * 0.92
battery_energy_used = delivered_energy / 0.92
For a grid-charge-then-discharge cycle, the effective AC-to-AC efficiency is approximately:
0.92 * 0.92 = 0.846
That means a buy/sell or buy/consume decision needs roughly an 18% higher future value before round-trip losses are covered:
future value >= buy price / (0.92 * 0.92)
Examples:
| Buy price | Approximate break-even future value |
|---|---|
| 10c/kWh | 11.8c/kWh |
| 20c/kWh | 23.6c/kWh |
| 30c/kWh | 35.4c/kWh |
With Learn battery efficiency enabled, PowerSync observes aggregate AC
battery power and SOC during normal operation. It accepts an efficiency sample
only after the battery completes a meaningful charge/discharge loop and returns
to the same SOC band. Learning remains at the legacy 92% value until there
are at least five valid loops across three local days and at least 1.5
equivalent full cycles. Implausible values, telemetry gaps, SOC/power direction
mismatches, near-empty/full SOC periods, and changed battery topology are
rejected or reset.
The learned value changes gradually and is used for physical charge, discharge,
and SOC conversion. Economic decisions remain at least as conservative as the
legacy 0.92 × 0.92 = 84.64% hurdle, so a high measured physical efficiency
cannot create newly permissive grid-charge/export arbitrage. Turning the
setting off keeps collecting evidence but restores the legacy value used by the
optimizer.
Current learner state is available in the efficiency_learning attribute of
the Battery Power Forecast sensor. It includes confidence, valid cycle/day/EFC
counts, candidate and applied efficiency, measurement boundary, and bounded
rejection reasons.
PowerSync does not currently add an explicit battery degradation or cycle-wear cost, such as 2c/kWh cycled. The optimizer accounts for energy losses, reserve constraints, inverter power limits, export permission windows, and stored-energy acquisition cost, but it does not currently price long-term battery wear as a separate cost.
For self-consumption, stored energy is valuable when it avoids a future import price high enough to justify the efficiency loss. For export, the optimizer checks whether the export value is worthwhile compared with import price and stored-energy acquisition cost. This helps avoid exporting energy below the best known cost of replacing it.
The LP is bounded by physical and user-configured constraints:
- Power balance must hold for every slot: solar, grid import, and battery discharge must cover load, grid export, and battery charge.
- SOC must remain between the optimizer backup reserve and 100%.
- Battery charge and discharge power cannot exceed the configured limits.
- If Maximum grid import is set, total planned site grid import cannot exceed that cap. Feasible battery grid charging is calculated from the remaining site headroom after forecast home load and solar are applied.
- If grid charging is disabled, battery charging is limited to solar surplus.
- When intentional battery export is not allowed, grid export is capped to solar surplus only.
- Battery discharge is limited to home self-consumption unless the slot is explicitly allowed for battery-to-grid export.
- Export must be backed by solar surplus or battery discharge; the LP cannot invent grid-import-to-export arbitrage.
- Same-slot charge/discharge loops are blocked.
- Charge can be blocked inside export-only windows so the battery charges before the window rather than during it.
- Profit Max can add a pre-window SOC floor so the battery targets full SOC before a known high-value export period.
If the current SOC is already below the optimizer reserve, the normal LP would be infeasible. PowerSync temporarily uses the current SOC as the effective reserve for that solve. That lets the LP plan recovery without allowing the battery to discharge any further. An infeasible first calculation can use a native self-consumption fallback without authorizing battery export or grid charging. A failed calculation without a usable result can instead use the greedy scheduler.
If a later command-mode projection fails, PowerSync can retain the previous usable plan. From v2.12.1395, the log and optimization JSON distinguish a timeout or iteration limit from genuine infeasibility. The retained plan keeps its own calculation status; mode_projection_failure_status and mode_projection_failure_message describe the failed later pass. PowerSync still checks the retained plan against the charging deadline and removes its reserve recommendation if that deadline is unmet. A retained plan or solver warning does not prove a hardware command or physical result.
Battery-to-grid export is not allowed everywhere. PowerSync builds a per-slot export mask from:
- Positive export-price opportunities.
- Flow Power Happy Hour when Profit Max is enabled.
- Export Boost windows.
- Joined saving-session windows.
Solar surplus can still export when battery export is not allowed. The restriction is specifically about spending stored battery energy for grid export.
The LP also checks acquisition cost. If stored energy was likely bought from the grid and the export price is below that acquisition cost, discharge is restricted to home self-consumption rather than battery-to-grid export. For mixed same-day inventory, PowerSync values the measured grid-origin share at its actual cost, proven solar-origin energy at zero acquisition cost, and any unknown carry-over conservatively from import prices; a small grid top-up does not price the whole battery at that one sample's rate. That conservative valuation reads only the intervals your provider actually published. Where a provider interval carries no usable price, the planner gap-fills it by repeating the neighbouring price so the solver has a complete series, but those copied values are excluded from the acquisition-cost reference and cannot move the valuation.
If Spread Export Across Window is enabled on a target-power-capable battery, PowerSync post-processes the LP schedule after these export permissions are applied. For each contiguous allowed export window, it sums the battery-export energy the LP already planned, then spreads that Wh value evenly across eligible slots as flat export actions. Slots at or below the applicable Auto Reserve export floor are not used for spreading. This happens independently of Profit Max; Profit Max only changes how willing the LP is to plan export and which Flow Power pre-window targets apply.
The LP solves power flows, not Home Assistant service calls. After solving, PowerSync maps the flow result to user-visible actions:
| Action | Technical Meaning |
|---|---|
charge |
Grid import is greater than home load and battery charge power is above the action threshold. Free import slots are forced to max charge. |
export |
Battery discharge and grid export are both above the action threshold. |
idle |
The LP wants to hold useful SOC for a future opportunity while the home draws from grid. |
self_consumption |
Natural battery operation: solar charging, battery serving home load, or no forced action needed. |
off_grid |
A Tesla-only post-LP curtailment overlay physically disconnects the site from grid during eligible low-value export periods. |
The action threshold is 100 W, which avoids rapid action changes from tiny solver values. A short export window near this threshold may earn less than one cent, so its displayed estimate can round to $0.00. That is a small planned export, not evidence that a command ran. Minimum export price can exclude low-price battery exports, but it is a price threshold rather than a minimum dollar return per window. Forecast updates can change or remove future windows.
A forecast showing export value above import cost can support charging now and exporting later when enabled and feasible. PowerSync does not schedule simultaneous grid import and export as a pass-through profit strategy; its battery, site-flow, efficiency and ownership constraints still apply.
idle is deliberately different from self_consumption. In idle, PowerSync is trying to hold SOC for later, so it may use a hold mode or a temporary reserve change. In self_consumption, the battery should behave naturally and serve the house, subject to the hardware reserve.
With Disable idle mode enabled for any provider, PowerSync converts planned idle actions to self_consumption after the LP solve, before the schedule is displayed or executed.
After a solve, PowerSync immediately executes the current slot. The polling loop then keeps re-solving and reapplying actions on 5-minute boundaries.
Execution is battery-system specific:
-
chargecalls the controller's force-charge path for the current action window. -
exportcalls the force-discharge path. By default, PowerSync requests the configured maximum discharge power because many inverters use that value as an export limit. When Spread Export Across Window is enabled on a supported target-power battery, PowerSync uses the post-processed action power instead. -
idleuses the safest available hold mechanism for the battery system. This can be a native hold/backup mode, a temporary backup reserve change, or self-consumption mode when no stronger hold is available. -
self_consumptioncalls the battery's self-consumption or restore-normal path and avoids unnecessary repeat API calls when the hardware is already in the right mode. -
off_griddelegates to the Tesla local curtailment fallback, which enforces pairing, SOC, duration, and reconnection checks.
When leaving idle, PowerSync restores the pre-idle reserve or work mode before applying the new action. This prevents a temporary hold value from becoming the user's permanent hardware reserve.
FoxESS (v2.12.1243+): IDLE selects Backup mode, which prevents discharge into household load, without raising the inverter's minimum SOC. A failed or unavailable Backup command is retried instead of using a reserve change as a fallback. This applies to the direct Modbus, foxess_modbus entity, and FoxESS Cloud connections.
If an older hold left minimum SOC elevated after a Home Assistant restart, update PowerSync and reset the inverter's minimum SOC to your intended value once. The update does not automatically lower an existing setting because it may have been set by you or another controller. A FoxCloud Mode Scheduler or third-party-control lock is separate: if the setting is blocked or returns again, check its control ownership and capture logs covering the recurrence.
The optimizer backup reserve is a software floor. It means the LP must not plan forced discharge/export below that SOC, and the executor must stop forced/max discharge once the battery reaches the floor.
At or below the optimizer reserve, PowerSync should return the battery/inverter to self_consumption, not keep it in an optimizer discharge/export mode and wait for hardware to stop it. This contract applies across supported battery systems.
The hardware backup reserve is separate. Where supported, it is a physical or manufacturer-enforced floor. It is best treated as a lower safety net, usually a few percent below the optimizer reserve. The optimizer reserve controls scheduling intent; the hardware reserve is a last line of defence.
Auto-apply optimizer reserve works only on the optimizer reserve side of that boundary. It may change the software floor used for planning and execution, but it never changes the hardware reserve or calls the battery reserve service. Turning it off restores the saved manual optimizer reserve. When Profit Max is enabled, forced export still uses the live optimizer floor, including any forecast-driven reduction.
Before executing the LP action, PowerSync applies runtime guards:
- Blocks forced export/discharge when current SOC is at or below the optimizer reserve.
- Blocks forced export/discharge when the planned action would project SOC at or below the applicable reserve floor.
- Rechecks the current effective export value, quota eligibility and configured price floor; it can replace an export plan with self-consumption when the runtime price/control guards no longer permit export.
- Skips force charge/export during suspected battery calibration.
- Overrides idle to self-consumption during demand-charge windows so the battery helps reduce grid import.
- Blocks export near demand windows when preserving battery is more valuable.
- Reconnects from off-grid before any action that needs the grid.
- Retries pending reserve restoration after IDLE until it succeeds.
- Cancels optimizer-created force modes if the new LP plan changes direction.
Manual force modes are tracked separately from optimizer-created force modes so a user command is not accidentally treated as an LP decision.
The LP is not only chasing the highest price in the visible chart. It also accounts for:
- Round-trip efficiency losses.
- The cost or opportunity cost of replacing stored energy later.
- The optimizer backup reserve.
- Whether battery export is allowed in that slot.
- Whether solar is forecast to refill the battery.
- Whether the export price is real, boosted, contractual, or speculative.
- Upcoming EV charging and home load.
- Demand-charge protection and other overlays.
- Battery-specific execution limits.
For example, the optimizer may hold SOC during a moderate-price period if a stronger export window is forecast later, or it may avoid export if the sell price does not beat the value of using that energy for home load after efficiency losses. Profit Max reduces that future-SOC penalty, so it is more willing to spend stored energy in high-value export windows, but it still respects reserves, export permissions, and hardware limits.