Understand every recommendation.

What Energy Compass entities mean, how their values are calculated, and which assumptions sit behind the plan.

This guide describes the Home Assistant integration in v0.1.43. Examples use PLN; your installation uses its configured currency. Names are shown in English and Polish.

Reading the compass

Energy Compass answers two different questions. Consumption compass estimates whether additional household use is attractive during the current display interval. Energy compass summarizes what the optimized battery, solar and grid plan proposes for the first native interval.

Advisory by default. The integration does not charge the battery, change an inverter limit, curtail panels or execute notification actions, unless you enable the optional Deye controller, which then writes inverter settings through Solarman. Results assume that the proposed plan can be followed. Existing inverter behavior may produce different costs.

  1. Check validityForecast valid must be on. Check current guidance validity before using a consumption recommendation.
  2. Read the cost and levelThe cost describes an extra energy probe over a time interval, not the electricity tariff at this instant.
  3. Inspect the planReview flows, future windows, source coverage and equipment assumptions.

kW measures power; kWh measures energy. An 8 kW flow lasting 15 minutes transfers 2 kWh. Plan flows are interval energies in kWh, not live power readings.

On narrow screens, scroll tables horizontally to read all columns. Keyboard users can focus a table region and use the arrow keys.

Inputs and assumptions

What feeds the calculation
InputMeaning and treatment
Import and export pricesChoose each side in Tariffs as a fixed rate, a numeric sensor/number/input_number state or attribute held constant across the known horizon, or an interval forecast. For a Polish installation the buy side also offers a built-in G11/G12/G12w tariff schedule (zones from the distributor's tariff, rates typed by you) and the sell side an RCE market price source floored at 0. Currency/MWh is divided by 1,000. Buy and sell transformations are independent; no currency conversion occurs.
Solar forecastInterval energy, or average power multiplied by elapsed hours. For example, 4 kW over 30 minutes becomes 2 kWh. Today/tomorrow continuations merge; only explicitly separate arrays add together. A daily total alone cannot place production in time. Disabled PV contributes zero.
Household loadA selected forecast, consumption energy statistic, power history or explicit daily estimate. Grid import alone is not whole-home load: it omits consumption supplied by PV or a battery.
Battery SOCCurrent stored energy, derived from a selected percentage, fraction or kWh source. Capacity, freshness, plausibility and optional independent BMS agreement are checked. Battery-disabled installations do not require SOC.
Battery constraintsCapacity, operating floor, ceiling, charge/discharge power, separate efficiencies and wear price. The operating floor must be at least the hardware floor. The solver uses the operating floor as its reserve.
Site constraintsInverter power, grid import and export limits, and explicit permission to model grid charging, battery export and curtailment. These are planning capabilities, not device commands.
Horizons and timingPlanning horizon, display horizon, percentile reference horizon, display interval, source freshness and compute budgets. Missing future coverage can shorten the usable plan.

Tariff transformation

effective price = normalized source price × multiplier × VAT factor + addition

VAT factor = 1 + VAT percent / 100 when enabled; otherwise 1

The addition is applied after the VAT factor. Fixed rates use the same transformation, and a built-in tariff schedule applies it to its peak or off-peak rate. A source with a price floor (the RCE market price shortcut) first replaces the normalized price by max(price, floor), so the floor sits before the multiplier, VAT factor and addition. Include applicable variable charges once; do not apply VAT again to a source that already contains it. Fixed monthly charges are reporting metadata and do not affect dispatch or the extra-kWh calculation.

Net-billing deposit value

Under net billing the deposit pays only the energy part of what is owed to the seller, and after 12 months a month's refund is capped at ρ × that month's export value (30 % with hourly RCE, 20 % with RCEm). While the cap does not bind, full prices are exact. Once it binds, one more unit of export is worth only ρ / 1.23 of a sell price that contains the 1.23 coefficient (0.244 for 30 %, 0.163 for 20 %) and the energy part of an import costs nothing in cash. The optional deposit model (deposit_model: off by default, or the experimental manual) gives the optimizer two planning-only factors, export_value_factor and import_energy_factor, and needs the distribution part of the buy price (buy_distribution_rate and buy_distribution_off_peak_rate, with buy_distribution_mode included or added on top of a dynamic energy price) to know the deposit-payable energy part. The distribution rates take VAT only, not the multiplier or addition; added puts them on top of the buy price for the planner and the reported costs alike. off is the setting for every site, also one whose deposit is forfeited: measured on a one-year replay of 40 hourly-RCE homes, no factor lowered the yearly bill by more than 44 PLN and the factor at the cap raised it in all 40 (by 68–431 PLN with the annual formula, 21–306 PLN with the monthly ledger); with RCEm the factor changes the bill by less than 10 PLN; switching battery export off because of a forfeited deposit cost 154–662 PLN a year in all 16 homes measured. The auto model is withdrawn and a stored auto is read as off. Reported costs always stay at invoice prices, and with the model off the plan is unchanged. Details and the diagnostics in dispatch_policy.settlement: guide.

Negative prices remain valid in the generic integration. Clipping an export price to zero or applying a deposit valuation factor requires an explicitly prepared source or suitable configuration; neither is an automatic assumption for every RCE installation. The PL net-billing setup profile pre-fills the 1.23 deposit factor only together with a PSE preset or an RCE market price sell source (which floors at 0 itself); it never clips prices on its own.

First setup requires separate confirmation of the displayed buy source and household load source. Every preview and save validates current inputs and solves a bounded base plan. An infeasible or timed-out plan cannot be saved; extra-consumption guidance is calculated after saving. Existing installations retain their original final confirmation. The first-setup form also offers three optional setup-profile selectors, for settlement, a Polish buy tariff and inverter tuning.

Load forecast from history

The Plan and Forecast valid sensors expose load_quality: selected source, actual forecast method, per-segment history sample counts, and fallback coverage. fallback_fraction measures elapsed forecast time, including separate repeated daylight-saving hours. Recorder fallback raises load_history_fallback even when its estimated energy is zero. This detailed attribute is excluded from recorder history.

The history model averages complete hourly consumption samples by local hour and day group. With group_weekends enabled, weekdays share one group and weekends another; otherwise each weekday has its own group. Defaults use 14 days of history and at least two samples per bucket. forecast_half_life_days (default 0, off) weights each sample by 0.5^(age / half-life) so the profile follows a level change, such as the start of the heating season, within a few days; the result remains a mean. A synthetic backtest (benchmarks/load_forecast_backtest.py) with a half-life of 5 days cuts the morning error after a level step by about a third while keeping the evening error within 5 % of the plain mean; run it on your own recorder before enabling the option.

Each forecast slot receives its elapsed share of the matching hourly mean. Recorder statistics use reset-adjusted cumulative sums; power history is integrated over complete hours with coverage checks. Missing buckets use fallback_daily_kwh / 24 only when fallback is enabled. An explicit fixed daily estimate is also distributed uniformly at daily_load_kwh / 24 kWh per elapsed hour.

Timestamp identity and durations use UTC; day grouping uses the configured local timezone. Repeated daylight-saving hours remain distinct intervals.

Generic integration defaults and configurable limits

These are setup defaults in settings.py, not a statement about your saved options. The standalone engine helper has different default absolute thresholds; Home Assistant passes the configured values explicitly.

  • Planning, display and reference horizons: 24 hours each; configurable from 1 to 48 hours. Display and reference horizons cannot exceed the requested planning horizon.
  • Display interval: 60 minutes; options are 15, 30 or 60 minutes. At most 96 distinct probes are allowed across display and reference horizons.
  • Extra-energy probe: 1 kWh; UI range 0.01–5 kWh.
  • Flexible-load profiles: 3, 5, 10, 15 and 20 kWh; default power cap 3 kW and allowed block-price degradation 15%.
  • BOOST ceiling: 0.01 currency/kWh. LIMIT floor: 1 currency/kWh. CHEAP/LIMIT percentiles: 25th/75th.
  • Probe-failure policy: absolute_fallback. Grid charging, battery export and curtailment: disabled.
  • Default budgets: 10 seconds for the base plan, 2 seconds per probe and 60 seconds overall. Performance allows up to 30 seconds for each solve and 300 seconds overall. Probes share the remaining time after the base calculation and a one-second finishing reserve. A probe that does not finish within its limit is unknown, never approximated. With self_sufficiency or backup_ready one probe takes several seconds already on the default 24-hour horizon with quarter-hour prices, so at the default limits their consumption levels are unknown for nearly every interval and the short_coverage policy applies with reason reference_probe_failed; on a 48-hour horizon this holds for every strategy. The guide lists the measured limits and what helps.
  • Default periodic refresh: 15 minutes, configurable up to one hour. Significant source changes can trigger earlier recalculation; validity boundaries only validate sources and advance cached advice.
  • Optional setup profiles may pre-fill some of these values once, for a new installation only; see setup profiles in the guide.

How calculation works

1. Build a baseline plan

The integration aligns source intervals without averaging away tariff boundaries, then solves a mixed-integer optimization using SciPy HiGHS. Each slot chooses import, export, battery charge, battery discharge, curtailed solar and end-of-slot battery energy.

PV − curtailment + import + discharge = load + export + charge

next stored energy = previous stored energy + ηcharge × charge − discharge / ηdischarge

objective J = Σ(buy × import − sell × export + wear × (charge + discharge) / 2) − terminal credit

All flow terms are kWh per native slot. Prices and wear use currency/kWh. Charge and discharge efficiencies, ηcharge and ηdischarge, are fractions between 0 and 1.

  • Grid import and export cannot happen simultaneously within a slot; neither can battery charge and discharge.
  • Power limits become slot-energy limits by multiplying kW by elapsed hours. The inverter bounds the magnitude of PV − curtailment + discharge − charge; grid limits apply separately.
  • With grid charging disabled, charging cannot exceed forecast solar surplus. With battery export disabled, discharge cannot exceed the forecast household deficit after PV.
  • With curtailment disabled, its value is fixed at zero. Incompatible generation, load, battery and export constraints may then make a plan infeasible.
  • The default preserve_initial terminal rule requires final battery energy at least equal to initial energy. Its terminal credit is zero. In value mode, final energy is credited at terminal_value_per_kwh instead.
  • An enabled daily cycle cap limits AC-side charge-plus-discharge throughput to 2 × capacity × daily_cycles per local date, less today's observed throughput. SOC efficiency is modeled separately; DC counters need upstream conversion. Zero disables the cap; a positive cap requires a current observed-throughput source.

The solver normally proves an optimum within its limit; if the limit runs out first, the best validated solution found so far is published with time_limited and its mip_gap. Returned flows are independently checked with a 0.000001 kWh absolute tolerance. These are AC-equivalent energy flows; equipment efficiency and usable capacity still need validation against measurements.

2. Add hypothetical consumption

For every display/reference interval, the integration adds a probe—normally 1 kWh—to forecast household demand. The added energy is spread uniformly in time across that interval and allocated to native slots by elapsed overlap.

extra load in slot = probe kWh × overlapping seconds / display-interval seconds

extra-kWh cost = (J with extra load − baseline J) / actual added kWh

It then reoptimizes the whole available planning horizon with prices, starting SOC, terminal rules and equipment limits unchanged. Extra consumption can cause more import, less export, different battery use or different future charging. The resulting cost includes those opportunity costs and any change in wear or terminal credit.

The interval starts at the snapshot time. With a 60-minute display interval, a snapshot at 21:35 covers 21:35–22:35. It can cross a tariff change at 22:00. It is not a price for consuming the entire extra kWh immediately.

A 1 kWh probe over 60 minutes adds an average 1 kW; over 15 minutes it adds 4 kW. Different probe sizes can produce different per-kWh costs when constraints bind. The last interval overlapping source coverage is clipped and probed at its actual duration without scaling down the configured energy. A very short tail therefore requires higher average power and can fail when equipment limits bind. That tail remains unknown while successful full reference intervals retain percentile guidance. Later intervals, infeasible probes and expired probe budgets remain unknown; they are not assigned a zero cost.

3. Compare with the reference horizon

Successful reference costs are sorted and configured percentiles are calculated by linear interpolation: for percentile p, position (n − 1) × p / 100 lies between two sorted costs. A failed source-clipped tail is excluded when at least one reference probe succeeded; a failed full interval invokes the configured policy. The minimum purchase tariff is taken independently from source slots overlapping the available reference range. Classification uses the rules in Consumption compass.

Reference coverage concerns the percentile horizon, not necessarily the entire planning horizon. If contiguous source coverage is shorter than the configured reference horizon, percentiles use that available coverage and the result reports it explicitly. Missing future prices do not by themselves invoke the probe-failure policy.

All 16 entities

Each installation creates 13 sensor definitions, two binary sensors and one strategy select, plus two sensors and one select when the optional Deye controller is enabled. Actual entity IDs depend on the entry name, language and registry history; use the entity picker or Developer tools → States to find yours. The keys below are stable internal identifiers, not literal entity IDs.

Expose costs controls the three cost sensors. Expose windows controls the four next_… sensors. A disabled entity is different from an enabled entity whose value is unavailable.

Entity index
EntityValueQuestion answered
Consumption compassBOOST / CHEAP / NORMAL / LIMITHow attractive is extra use in this display interval?
Consumption costCurrency/kWhWhat does the extra-energy probe cost per kWh?
Flexible energy depthkWhHow much flexible energy fits before marginal price degrades materially?
Next changeTimestampWhen does the consumption level next change?
Next boost startTimestampWhen is the active or next BOOST window?
Next cheap startTimestampWhen is the active or next CHEAP window?
Next limit startTimestampWhen is the active or next LIMIT window?
Energy compassOne of six energy statesWhich flow dominates the first planned slot?
PlanSnapshot timestamp + attributesWhat are the detailed flows and opportunities?
Expected net costCurrencyWhat are forecast import costs minus export value?
Expected wear costCurrencyWhat battery wear does the plan assign?
Optimizer statusRuntime statusDid the baseline calculation succeed?
Battery balanceok / eligible / scheduled / holding / overdueIs a periodic LFP balance hold due, planned or in progress?
SOC estimatorPercentWhat SOC do the battery energy counters give, and when was it last anchored?
Forecast validon / offDoes a published plan still cover now?
Alerton / offWhy did the latest input check or calculation fail?
StrategyOne of six strategiesWhich objective bundle does the optimizer solve?
Deye controllerTimestampWhen must the Deye controller blueprint run next?
Deye modeOff / Simulation / AutoIs the Deye controller allowed to write?
Deye controller runtimeRuntime codeWhat did the Deye controller last decide and confirm?
Pause inverter writesOn / offIs every inverter write held?

Consumption compass

consumption_compass · Kompas zużycia · Sensor

The level of the first consumption outlook interval. Let c be its extra-kWh cost, B the BOOST ceiling, L the LIMIT floor, Qlow/Qhigh the configured reference percentiles, and Pmin the minimum purchase tariff in the available reference horizon. Rules are evaluated in the order shown; the first match wins.

StateExact rule with complete referenceMeaning
BOOSTc < BExtra use costs less than your configured BOOST ceiling. The default ceiling is 0.01 currency/kWh; real negative costs remain BOOST.
CHEAPc ≤ Qlow or c ≤ PminExtra use is no more expensive than the low percentile or the minimum purchase tariff in the available reference horizon.
LIMITc > L and c > QhighExtra use exceeds both your absolute expensive threshold and the upper reference percentile.
NORMALAll other known costsNone of the other rules matched. This is not a statement that the battery is full or the instantaneous tariff is low.

BOOST wins first, followed by CHEAP, LIMIT and NORMAL. Equality at the low percentile or minimum purchase tariff is CHEAP; a negligible numeric tolerance covers solver residue at those equalities. Equality at the BOOST ceiling is not BOOST, and equality at the LIMIT floor or high percentile does not trigger LIMIT.

With absolute_fallback, a failed full reference probe removes percentile tests but retains the independent minimum-tariff CHEAP rule. BOOST uses c < B; CHEAP uses c ≤ Pmin; LIMIT uses c > L; everything else known is NORMAL. With the unavailable policy, a full-interval failure suppresses levels. A failed source-clipped tail remains unknown without suppressing percentile advice from successful reference probes.

Unavailable: the baseline is invalid/expired, or the current guidance level is unknown. A low SOC is incorporated into the optimization; it does not independently force LIMIT.

Consumption cost

consumption_cost · Koszt zużycia · Currency/kWh

The finite-difference cost of the current display interval: (probe objective − baseline objective) / added kWh. Positive means extra modeled cost; zero means no change; negative means the extra load reduces the modeled objective. This is neither an electricity-meter reading nor the current buy rate.

Attributes include method: finite_difference and probe_kwh. Display rounding uses cost_precision; classification uses the unrounded value.

Unavailable: same gate as Consumption compass. Under the strict unavailable policy, this sensor is unavailable even if a numeric probe result survives in plan.outlook but its level is unknown.

Flexible energy depth

flexible_energy_depth · Głębokość elastycznego zużycia · kWh

Five independent counterfactual plans add 3, 5, 10, 15 and 20 kWh of flexible demand to the same baseline. Each profile can schedule at up to the configured power cap, 3 kW by default. The optimizer includes tariffs, PV, battery losses and wear, terminal value, grid limits and operating constraints.

anchor price = (objective3 − baseline objective) / 3

allowed block price = anchor price + 15% × |anchor price|

block pricea→b = (incremental costb − incremental costa) / (b − a)

The published state is the largest consecutive profile whose next block stays at or below the allowed price. The first degraded or unknown block stops depth; a later stable block cannot skip that gap. Negative anchors use the same absolute-difference rule, so −0.10 permits values through −0.085. A zero anchor permits no unfavorable positive increase.

Attributes expose the 3 kWh anchor price, allowed block price, configured power and degradation, currency, and all profile summaries. Profiles are grouped as small (3/5 kWh), medium (10/15 kWh) and large (20 kWh). Each includes total incremental cost, average and marginal block prices, import/export and battery-throughput deltas, status, failure reason and nonzero schedule rows.

This sensor measures price depth, not whether electricity is cheap in absolute terms. A stable depth can still carry an expensive anchor price. Compare anchor_price_per_kwh with the tariff or household policy before starting a load.

Unavailable: the baseline is invalid/expired, flexible-depth analysis is disabled, or the 3 kWh anchor cannot be solved within coverage, power and time limits.

Next change

next_change · Następna zmiana · Timestamp

The start of the first later outlook interval whose known consumption level differs from the first interval. Attribute next_level gives that level. It tracks consumption guidance, not an energy-state change or every tariff change.

The scan stops at the first later unknown level. If the current level itself is unknown, the entity implementation can return the immediately following known level; interpret this as the next known guidance, not a confirmed transition from a known current state.

Unavailable: no qualifying transition appears before the scan stops, or the baseline is invalid/expired.

Next boost start

next_boost_start · Początek zwiększonego zużycia · Timestamp

The start of the first active or upcoming contiguous BOOST window. Adjacent BOOST intervals merge. An active window can have a start timestamp in the past; it is not skipped in favor of tomorrow's window.

Unavailable: no BOOST window exists in the calculated outlook, or the baseline is invalid/expired. This does not assert that BOOST can never occur beyond forecast coverage.

Next cheap start

next_cheap_start · Początek taniego okresu · Timestamp

The start of the first active or upcoming contiguous CHEAP window. BOOST windows are separate; this entity does not mean “any favorable period.” The plan's favorable_windows combines adjacent BOOST and CHEAP intervals.

Unavailable: no CHEAP window exists, the baseline is invalid/expired, or the classification mode cannot produce CHEAP.

Next limit start

next_limit_start · Początek ograniczenia · Timestamp

The start of the first active or upcoming contiguous LIMIT window. It indicates an expensive additional-consumption period; it does not restrict inverter power or shut off household loads.

Unavailable: no LIMIT window exists in coverage, or the baseline is invalid/expired.

Attributes shared by the three window-start sensors

status
active when now falls inside the window; otherwise upcoming.
end, duration_minutes, remaining_minutes
Window end, total duration and time remaining. Before an upcoming window starts, remaining minutes equals its full duration.
start_basis
forecast for a predicted start; first_observed for an active window anchored when first observed. Overlapping active starts are preserved across recalculation and restart while the stored anchor remains applicable.

Home Assistant can omit dynamic attributes from unavailable entities. For absent windows, inspect plan.window_status: none_in_coverage, active or upcoming. Known later windows can remain available after a failed current probe.

Energy compass

energy_compass · Kompas energii · Sensor

One primary label for the first native plan interval. This interval can be shorter than the consumption display interval. Rules use interval kWh and a tolerance ε = 0.000001 kWh, in this exact precedence:

StateRuleInterpretation
CURTAILcurtailment > εSome available solar production is left unused in the plan. Takes precedence over any simultaneous battery flow.
CHARGE_GRIDcharge > max(PV − load, 0) + εCharging exceeds available forecast solar surplus, indicating grid-fed charging. Some solar can also contribute.
CHARGE_PVcharge > ε, after the rules aboveBattery charging fits within forecast solar surplus.
DISCHARGE_GRIDdischarge > ε and export > εBattery discharge coincides with aggregate grid export. Aggregate flows do not uniquely prove which source supplied each exported kWh.
SELF_CONSUMEdischarge > ε, without the conditions aboveBattery discharge serves household demand without material grid export.
HOLDNo rule above matchedNo material battery charge/discharge or curtailment. PV can still power the home or export; the home can still import.

Pure solar self-consumption with an idle battery is HOLD, not SELF_CONSUME. Labels summarize proposed aggregate flows and do not confirm inverter mode, command execution or measured power.

Attributes contain the first interval's start, end, buy_per_kwh, sell_per_kwh, pv_kwh, load_kwh, grid_import_kwh, grid_export_kwh, charge_kwh, discharge_kwh, curtail_kwh and end_soc_kwh. The last value is stored energy, not a percentage.

Unavailable: the baseline is invalid/expired. A failed consumption probe alone does not hide a valid energy plan.

Plan

plan · Plan · Timestamp

The entity state is generated_at: the input snapshot time used to generate the plan, not solver completion time or planned end time. Detailed output lives in attributes.

AttributeContents
intervalsBaseline native slots: timestamps, prices, state, PV/load, grid flows, battery flows, curtailment and end SOC in kWh. Same fields described under Energy compass.
outlookDisplay intervals with start, end, cost_per_kwh and level. Failed/uncovered probes contain null cost and level. Strict unavailable classification can retain a numeric cost with null level.
flexible_load_profilesIndependent 3/5/10/15/20 kWh profile costs, statuses, deltas and nonzero schedules. Excluded from recorder history.
windowsLists under boost, cheap, limit and favorable. Each window has start/end timestamps and the levels it contains.
favorable_windowsCombined adjacent BOOST/CHEAP windows; equivalent to windows.favorable.
window_statusPer-kind active, upcoming or none_in_coverage.
measurementsDiagnostics for selected optional measurements, including availability. These do not turn planned flows into live measurements.
presentationConfigured colors, precision, label and cost/window exposure preferences.
notification_preferencesOpt-in preferences for the optional notification blueprint. The integration itself sends no messages.
dispatch_policy.settlementNet-billing deposit model diagnostics: model, the two factors the optimizer used, cap_pressure, refund_percent, distribution_mode and reason (ok, off, split_missing).
monthly_charge_reporting_onlyConfigured fixed monthly charge, excluded from the optimizer and cost sensors.
balance_pv_seriespessimistic or median: the PV series that decided the LFP balance window candidates (see the optional pessimistic PV source); null when the balance is off or there is no battery.
attribute_schema_version3; adds strategy, autonomy_shortfall_kwh and cap_violation_kwh; peak_reserve_shortfall_kwh is additive (shortfall below the peak-period reserve set by peak_reserve_margin_kwh and export_reserve_margin_kwh, default 0 = off); so are time_limited (base plan accepted at solve_time_limit_s instead of a proven optimum) and mip_gap (the solver's relative gap: 0 = proven optimal, normally 0, or at most 0.0001 when no episode hurdle is active, for a proven plan; not a hard bound, because HiGHS also stops on its absolute gap of 1e-6, so when the weighted objective the solver minimises is within about ±0.01 of zero the relative gap can be larger, up to roughly 1e-6 / |objective|, which exceeds 1 once the objective is within 1e-6 of zero; that objective is the plan's cost plus the episode reserves and the strategy's penalty terms, so it can be near zero while the plan's cost is not, and the reverse; time_limited, not mip_gap == 0, tells a proven plan from an unproven one).

Large forecast arrays are excluded from recorder history by the integration. Do not assume historical entity states retain the full plan arrays.

Unavailable: the baseline is invalid/expired. Expired previous plans are kept for diagnostics, not published as current advice.

Expected net cost

expected_net_cost · Przewidywany koszt netto · Currency

Σ(import kWh × buy price − export kWh × sell price)

Calculated from the baseline over the display horizon, shortened to available coverage. A slot crossing the display end is counted proportionally. Attribute coverage_hours gives the actual duration.

Positive means modeled net spending; negative means export value exceeds import cost. “Net” means imports minus exports, not “excluding VAT.” Export value may represent a settlement credit rather than cash paid out.

This value excludes battery wear, terminal credit and fixed monthly charges. It is not automatically equal to the full optimizer objective or the final electricity bill.

Unavailable: the baseline is invalid/expired.

Expected wear cost

expected_wear_cost · Przewidywany koszt zużycia baterii · Currency

Σ(wear price × (charge kWh + discharge kWh) / 2)

Uses the same display coverage and proportional boundary treatment as Expected net cost, with coverage_hours. Wear is a configured economic assumption, not a battery-health measurement or supplier fee.

At 0.05 PLN/kWh, charging 2 kWh and discharging 1.9 kWh costs 0.05 × (2 + 1.9) / 2 = 0.0975 PLN. A battery-free plan has zero wear.

Unavailable: the baseline is invalid/expired.

Optimizer status

optimizer_status · Stan optymalizatora · Diagnostic sensor

StateMeaning and next check
readyA baseline result was published. Check Forecast valid for freshness and current guidance validity for the consumption probe; ready does not promise complete reference coverage.
calculatingSetup, changed inputs or a time boundary triggered a new calculation. Previously published values remain visible with refreshing=true while a replacement is queued or running; Forecast valid remains on while that plan covers now. An existing alert stays on until successful replacement.
invalid_inputA required input or configuration failed validation. Inspect reason for missing/stale sources, units, SOC checks, history coverage or invalid settings.
timeoutThe base solve or overall calculation exceeded its budget. Check timing, model size and hardware. A single failed probe can instead leave a ready baseline with incomplete guidance.
infeasibleNo baseline plan satisfies all constraints. Check demand/generation, SOC, reserve, power/export limits, terminal requirements and allowed capabilities.
errorAnother solver failure or unexpected exception occurred. Inspect reason and integration diagnostics/logs.
insufficient_dataReserved in translations, but not emitted by the 0.1.43 coordinator. Missing-data validation currently reports invalid_input.

This entity remains available during calculation failures. Attribute expired_previous_generated_at identifies the last invalidated plan snapshot when one exists.

When the optional lifetime counters battery_charge_energy and battery_discharge_energy (battery round trip) or pv_energy (PV forecast accuracy) are bound as Sources measurements, attribute measured_calibration (also in the integration diagnostics) reports the measured round trip over 30 days next to eta_charge × eta_discharge and the real ÷ forecast PV ratio over 14 days, in total and per local hour. It is read-only and never changes a plan; without those sources the attribute is absent. See the guide.

Battery balance

battery_balance · Balansowanie baterii · Diagnostic sensor

Tracks the periodic LFP balance hold: a full-charge dwell that lets a lithium iron phosphate pack's cells resettle voltage-wise. Disabled by default; enable it with the LFP balance setting.

StateMeaning
okThe last hold completed recently; no balance is due yet.
eligibleA balance is due and could start once SOC reaches the threshold, but none is planned yet.
scheduledThe optimizer has planned a balance window before the interval fills.
holdingSOC is at or above the threshold now; the hold timer is running.
overdueThe balance interval elapsed without a completed hold and none fits the remaining horizon.

Attributes: last_completed, next_due, days_overdue, planned_start, planned_end, planned_mode, pv_series (median or pessimistic: the PV series that decided the window candidates; see the optional pessimistic PV source), hold_progress_minutes, hold_required_minutes and threshold_percent.

Unavailable: only before the balance tracker has loaded from storage.

SOC estimator

soc_estimator · Estymator SOC · Diagnostic sensor

The battery SOC computed from the lifetime charge and discharge energy counters when Battery SOC source is Counter estimator (or while its Shadow run compares it with a bound SOC entity). Created only then; turning it on or off reloads the entry. The state is a percentage and is recorded; of the attributes only anchor_kind is recorded.

Attributes: energy_kwh, anchor_kind (initial, seed, full, empty, reset), anchor_at, anchor_age_h, throughput_since_anchor_kwh, pending_kwh, unanchored_top (the count reached the capacity without a full anchor, so the state is 99.9 %), excess_kwh (the energy above the capacity, kept), full_candidate, measured_capacity_kwh, eta_charge, capacity_kwh, mode (counter_estimator or shadow), bms_soc (comparison only), full_candidates and anchors (the last 20 of each). See the guide.

Unavailable: while the counters cannot be read.

Alert

alert · Alert · Diagnostic problem binary sensor

on: an input check or calculation failed. code identifies SOC jumps, invalid input, timeout, infeasibility or calculation errors; reason provides detail. since marks the first occurrence of the current problem, plan_retained identifies a covered previous plan, and last_successful_plan_at identifies its generation. A retry keeps the alert; successful publication clears it.

A corrected SOC can establish a new reference after fresh reports remain physically plausible for at least 60 seconds. Both the first and preceding recovery reports are checked. Missing/invalid readings or a new jump interrupt stabilization. Repeating the same timestamp is insufficient. The existing plan continues during this process.

Forecast valid

forecast_valid · Poprawna prognoza · Binary sensor

on: a valid baseline exists and current UTC time is strictly before valid_until. off: no published plan covers the current time. This entity remains available to report that condition.

Normally valid_until is bounded by periodic recalculation, forecast coverage and source freshness. During a refresh or failure, plan_retained=true keeps the last plan available until its actual coverage end; Alert reports input and calculation problems separately. Native interval boundaries advance current advice and commitment clocks without a new optimization. Fresh reports of unchanged inputs can extend freshness within those bounds; significant changes still request an earlier solve. An unchanged numeric SOC still needs a trustworthy report timestamp; availability is not established by its percentage alone. The default auto policy uses Home Assistant’s native last_reported for native SOC and BMS timestamp paths, including older saved last_updated paths. An identical fresh report therefore stays fresh. Custom timestamp paths retain their exact meaning; exact_path also preserves literal native-field selection.

current_guidance_valid
Whether the current consumption outlook has a known level. Use it together with the binary sensor's on state; it is generation metadata and is not itself a clock-based freshness check.
coverage_end, requested_end, coverage_complete
Actual source horizon, requested planning end, and whether that entire requested horizon is covered. These are distinct from reference-horizon classification.
input_ages
Snapshot ages in seconds based on Home Assistant last_updated. Source-specific publication and SOC report timestamps can impose additional freshness checks.
missing_sources
Missing continuation identifiers or the invalidation reason, depending on whether a valid baseline exists.

Forecast valid does not certify tariff accuracy, battery capacity, perfect forecasts or physical execution. Those assumptions need separate validation.

Strategy

strategy · Strategia · Select

OptionGoal
cost_minMinimalizacja kosztów. Lowest forecast cost; the default, unchanged plain model.
self_sufficiencySamowystarczalność. Minimize grid kWh rather than currency; autonomy floor on.
backup_readyGotowość awaryjna. Keep at least the backup target (default 80 %) of capacity.
pv_swapZamiana energii PV. Buy cheap at night, export up to the day's PV later; Sell only PV forced on, grid-charge ceiling forced off.
max_exportMaksymalny eksport. Unrestricted arbitrage; export hurdle, Sell only PV and grid-charge ceiling forced off.
grid_friendlyPrzyjazna dla sieci. Penalize import peaks and power above soft import/export caps.

Strategies change planning weights and a few policy flags only; reported costs stay real currency. A new selection releases the carried operating-mode commitment for the next plan. The plan sensor reports strategy, autonomy_shortfall_kwh and cap_violation_kwh. Each strategy, its conditions and the automatic switch blueprint are described with diagrams in the states and strategies guide (wersja polska).

Deye controller

deye_controller · Sterownik Deye · Timestamp sensor, only with Options → Deye controller enabled

The state is the next instant the Deye controller must run: the earliest row end, the accepted plan's valid_until or coverage_end, or the next local TOU program boundary. Its attributes are the contract between Energy Compass and the executor (the native engine or the deprecated blueprint): controller_schema, mode, mode_entity, restore_pending, session, plan_reason (ok, session, revoked, sources), status, retained, generation, valid_until, coverage_end, accepted_at, revoked_generation, revoked_at, revoked_reason, the battery parameters capacity_kwh, eta_charge, eta_discharge, charge_kw and discharge_kw, device_id, program_prefix, tou_problem, next_tou, and the unrecorded accepted plan and tou values.

Unavailable: only while the entry is unloaded. The accepted plan is kept across an entry reload and dropped at a Home Assistant restart.

Deye mode

deye_mode · Tryb sterownika Deye · Select, only with the controller enabled

Off releases control after a confirmed restore of the base profile, Simulation computes every target without writing, Auto writes and verifies every register. The options are not translated. A fresh controller starts in Off.

Deye controller runtime

deye_runtime · Stan pracy sterownika Deye · Sensor, only with the controller enabled

The state is the runtime code (waiting, ok, blocked, restored, verification_required, write_failed, simulation_blocked, paused or error). The recorded attributes are restore_pending, reason, state, requested_mode, confirmed_mode, since, warning, takeover_blocked and accepted_generation; the full runtime and updated_at are not recorded. Detail-only changes are published at most once a minute.

Pause inverter writes

deye_pause · Wstrzymaj zapisy do falownika · Switch, only with the native Deye engine

While on, the controller computes and publishes the decision but sends no write. restore_pending and the uncertain registers stay as they are and the runtime code is paused. A run in flight stops before its next write; turning the switch off requests a run, and the usual rules decide between continuing and restoring the base profile. Mode Off with nothing to restore reports released even when paused; with a restore pending the pause wins. The flag is saved at once and is cleared when the engine is switched to blueprint.

Validity and missing data

Entities share generated_at, valid_until, reason, reasons, calibration, capacity_calibration and classification_mode attributes when available. reason describes invalidation or reference coverage; reasons combines quality warnings and any incomplete-reference reason.

Plan continuity. A queued or failed refresh keeps the previous covered plan operating with plan_retained=true. Native intervals and windows advance with time. refreshing=true means a replacement is queued or running. Input errors and failed calculations turn Alert on; they do not erase the plan. Successful publication replaces it and clears the alert. Coverage never extends beyond the last forecast interval. Unknown probes and ended windows remain unavailable. Restart or integration reload requires a new plan; saving options applies live and keeps the plan retained unless it changes which entities exist, the name or the currency; a running Energy Atlas is refreshed in place.

Classification modes
ModeWhat happened
percentileAvailable reference probes succeeded, or only a source-clipped tail failed while other reference probes succeeded. Absolute thresholds, successful-cost percentiles and the minimum purchase tariff apply.
absolute_fallbackA full reference probe failed, or no reference probe succeeded. Known costs use absolute BOOST/LIMIT and minimum-tariff CHEAP rules.
unavailableA full reference probe failed, or no reference probe succeeded, and the selected strict policy suppresses consumption levels. The baseline can remain valid.
Coverage reasons and quality warnings
ValueMeaning
completeThe reference horizon has all probe results. This alone does not mean the longer requested planning horizon is fully covered.
available_reference_horizonAll probes succeeded over contiguous source coverage shorter than the configured reference horizon; percentiles use that available range.
reference_probe_failedAt least one covered reference probe failed or ran out of time. Percentile mode can remain active when every failure is a source-clipped tail and other reference probes succeeded.
short_source_coverageAvailable planning coverage is shorter than requested. Inspect coverage_end.
missing_forecast_continuationA selected continuation, such as tomorrow's forecast, is unavailable. Usable contiguous coverage may remain.
current_guidance_unavailableThe current consumption level is unknown. Baseline flows/costs and known future windows may remain useful.
unvalidated_tariffTariff calibration is marked unvalidated. The model can calculate, but rates and settlement assumptions have not been marked verified.
unvalidated_capacityBattery capacity is marked unvalidated. This warning does not itself disable the baseline.

Calibration attributes are unvalidated or verified. They reflect configured validation status, not an automatic measurement or certification. Detailed input/solver failures can appear as other reason strings.

Unavailable is not zero, HOLD or NORMAL. Do not replace missing recommendations with a favorable default. An absent window may simply mean no matching period is known within current coverage.

For consumption-driven automations, check Forecast valid is on and current_guidance_valid is true, then compare stable uppercase levels. Optional window notifications also require Notify enabled and a configured action in the notification blueprint.

Worked examples

These are explanatory scenarios, not live readings, recommended tariff settings or guaranteed savings. Their values illustrate the formulas above.

Battery capacity is not available energy

Example assumptions
ParameterValue
Nominal battery / initial SOC25 kWh / 11%
Hardware floor / operating floor / ceiling5% / 10% / 100%
Charge / discharge / inverter limit8 kW / 8 kW / 8 kW
Grid import / export limit25 kW / 8 kW
Round-trip efficiency95%; ηcharge = ηdischarge = √0.95 ≈ 0.974679
Wear assumption0.05 PLN/kWh of average charge/discharge throughput
Capabilities / cycle capGrid charging and battery export allowed; curtailment disabled; no daily cycle cap
Terminal rulepreserve_initial; zero terminal credit

Initial stored energy = 25 × 0.11 = 2.75 kWh

Minimum stored energy = 25 × 0.10 = 2.50 kWh

Stored energy above reserve = 2.75 − 2.50 = 0.25 kWh

Deliverable energy above reserve ≈ 0.25 × 0.974679 = 0.24367 kWh

At 11% SOC, this 25 kWh battery has only about 0.244 kWh available to supply AC loads before the planning reserve, subject to power and other constraints. The optimizer cannot treat the nominal 25 kWh as immediately available.

Why NORMAL can coexist with a 1.25 PLN/kWh tariff

Use a deliberately simple battery-disabled, PV-disabled case to isolate the display-interval effect. Load is a fixed 24 kWh/day (1 kW), the grid import limit is 25 kW, export is zero, and no equipment limit binds. The snapshot is at 21:30; planning, display and reference horizons are 24 hours; probes are 1 kWh across 60-minute intervals.

Assume import costs 1.25 PLN/kWh from 06:00–22:00 and 0.61 PLN/kWh from 22:00–06:00, repeating daily. Export price and wear are zero; there is no battery terminal credit. BOOST ceiling is 0.01 and LIMIT floor is 1 PLN/kWh; reference percentiles are 25th/75th and all probes succeed.

Extra demand at 21:30–22:00: 0.5 kWh × 1.25 = 0.625 PLN

Extra demand at 22:00–22:30: 0.5 kWh × 0.61 = 0.305 PLN

Consumption cost = (0.625 + 0.305) / 1 kWh = 0.930 PLN/kWh

The 24 reference costs contain seven full low-rate hours at 0.61, two mixed hours at 0.93, and fifteen full high-rate hours at 1.25. Thus Q25 = 0.61 and Q75 = 1.25. The current 0.93 is above BOOST, below the LIMIT floor, and not below Q25: NORMAL.

Baseline import cost is 8 × 0.61 + 16 × 1.25 = 24.88 PLN. The probe increases the objective to 25.81 PLN; the difference is 0.93 PLN. Energy compass is HOLD, Expected wear cost is 0, and Forecast valid is on while this generation remains fresh.

The first fully low-rate display interval begins at 22:30 in this snapshot's outlook, not at the 22:00 tariff boundary, because display bins are anchored to 21:30. Low-rate bins equal both Q25 and the minimum purchase tariff, so they are CHEAP. High-rate bins equal Q75 and remain NORMAL. The mixed current interval remains NORMAL, with the next CHEAP window beginning at 22:30.

Adding a nearly empty battery changes feasible flows and costs, but introduces no independent “low SOC means LIMIT” rule.

Solar consumption can have an export opportunity cost

In a separate one-hour example, disable the battery and assume 2 kWh PV, 1 kWh household demand, a 0.40 PLN/kWh export value, a 1.25 PLN/kWh import rate and nonbinding inverter/grid limits. The baseline exports 1 kWh, so its net objective is −0.40 PLN.

A 1 kWh extra-load probe consumes that surplus. Import remains zero, export falls to zero and the objective becomes 0 PLN:

Extra-kWh cost = (0 − (−0.40)) / 1 = 0.40 PLN/kWh

No extra grid purchase occurred, but export value was lost. With a battery, the same comparison can also include changed charging, discharging, future purchases and wear. The integration recomputes the full plan instead of assuming every extra kWh costs the current buy rate.

Curtailment

Curtailment means deliberately leaving some available solar production unused. It differs from battery charging, exporting surplus or reducing household consumption.

Example: during one hour, PV could produce 8 kWh, the home needs 1 kWh, the battery is full and export is disallowed. A feasible plan with curtailment can use 1 kWh and curtail 7 kWh. Curtailment may also become economical with negative export prices, depending on the rest of the plan.

  1. Open Settings → Devices & services → Energy Compass → Configure.
  2. Open Hardware capabilities (Możliwości sprzętu).
  3. Enable Allow curtailment (Ograniczanie produkcji PV), then use Preview and save (Podgląd i zapis).

This allows curtail_kwh to be positive in the optimizer; it does not force curtailment or operate the inverter. Enabling a capability that equipment cannot follow makes the advisory plan unrealistic.

Physical curtailment needs a separate control layer. That layer needs compatible inverter generation/export controls, verified metering and operating-mode behavior, and a way to restore normal limits. Energy Compass v0.1.2 provides no such controller.

An 8 kW PV system with an 8 kW permitted export path usually has room to export surplus, subject to shared inverter and other limits. When export value is explicitly floored at zero, negative raw market prices alone do not create a modeled export penalty. Allowing curtailment therefore does not guarantee a CURTAIL state.

Source reference and further reading

This guide follows the released implementation. When updating calculations or entity behavior, update the corresponding explanations here.