This page shows where config.json values affect the EMS power regulation
chain. Use it as a map when tuning a value and asking:
If I change this config value, where exactly does it affect power control?
It does not replace the detailed configuration reference. For exact value descriptions and examples, see configuration.md and configuration-examples.md.
Some operator values can also be changed through runtime state or Home Assistant. In that case the runtime value is used during the cycle, but the control block shown here is the same.
The template profile is intended for normal standalone live control after real
local values are configured and installation limits are reviewed. If required
placeholders are still present, EMS forces safe mode: control disabled, dry-run
enabled, and hardware writes blocked. Set system.dry_run=true for an
optional no-write validation run.
Read the main path from top to bottom. Solid arrows show the order of one EMS
cycle, from measurements to a possible Zendure outputLimit write.
Dotted side arrows list the config values that influence that block. A value listed next to a block does not necessarily force a write; it only affects the decision made at that point.
flowchart TD
A[Runtime and safety state] --> B[Shelly house-load measurement]
B --> C[Zendure telemetry fetch]
C --> D[Device capability detection]
D --> E[SOC / mode reconciliation when due]
E --> F[Night / minSoc idle protection]
F --> G[Load filtering]
G --> H[Sign-change fast response]
H --> I[Total target calculation]
I --> J[Total ramp, bypass and stale telemetry handling]
J --> K[Device allocation]
K --> L[PV-first weighting]
L --> M[PV charge / SOC balancing]
M --> N[Battery top-up / battery weighting]
N --> O[Limits, clamping and redistribution]
O --> P[Device ramp]
P --> Q[Effective target and min output limit]
Q --> R[Deadband and write gates]
R --> S[Safety gates]
S --> T[Zendure outputLimit API write]
A -. config .-> A_CFG["system.enabled<br/>system.dry_run<br/>system.allow_hardware_writes<br/>ha.enabled<br/>ha.control_enabled"]
B -. config .-> B_CFG["shelly.ip<br/>system.loop_interval"]
C -. config .-> C_CFG["devices[].ip<br/>devices[].sn<br/>system.output_control.telemetry_max_age_seconds"]
D -. telemetry .-> D_CFG["PV, output, outputLimit, pack power<br/>socLimit, dcStatus, acStatus, packState"]
E -. config .-> E_CFG["system.soc_reconcile_interval<br/>system.allow_state_reconciliation_writes<br/>devices[].min_soc<br/>devices[].max_soc<br/>devices[].smart_mode"]
F -. config .-> F_CFG["system.min_output_limit<br/>devices[].min_soc"]
G -. config .-> G_CFG["system.output_control.filter_enabled<br/>system.output_control.filter_method<br/>system.output_control.median_window<br/>system.output_control.ema_alpha<br/>system.output_control.load_deadband_w"]
H -. config .-> H_CFG["system.output_control.sign_change_fast_response_enabled<br/>system.output_control.sign_change_threshold_w<br/>system.output_control.sign_change_filter_reset_factor"]
I -. config .-> I_CFG["system.max_total_power<br/>system.output_control.target_deadband_w"]
J -. config .-> J_CFG["system.output_control.ramp_enabled<br/>system.output_control.ramp_up_w_per_cycle<br/>system.output_control.ramp_down_w_per_cycle<br/>system.output_control.large_import_bypass_w<br/>system.output_control.large_export_bypass_w<br/>system.output_control.bypass_ramp_multiplier<br/>system.output_control.telemetry_max_age_seconds<br/>system.output_control.stale_telemetry_ramp_factor"]
L -. config .-> L_CFG["system.pv_kwp_weighting<br/>devices[].pv_kwp<br/>devices[].pv_priority_factor"]
M -. config .-> M_CFG["system.pv_charge_balance_enabled<br/>system.pv_charge_balance_deadband_percent<br/>system.pv_charge_balance_full_bias_percent<br/>system.pv_charge_balance_strength"]
N -. config .-> N_CFG["system.battery_kwh_weighting<br/>devices[].battery_kwh<br/>devices[].min_soc"]
O -. config .-> O_CFG["system.max_device_power<br/>system.max_total_power<br/>devices[].max_power<br/>system.redistribute_clamped_power"]
P -. config .-> P_CFG["system.output_control.device_ramp_enabled<br/>system.output_control.device_ramp_up_w_per_cycle<br/>system.output_control.device_ramp_down_w_per_cycle<br/>system.output_control.bypass_ramp_multiplier"]
Q -. config .-> Q_CFG["system.enabled<br/>system.min_output_limit<br/>devices[].max_power"]
R -. config .-> R_CFG["system.deadband"]
S -. config .-> S_CFG["system.dry_run<br/>system.allow_hardware_writes<br/>system.allow_state_reconciliation_writes"]
| Parameter | Control Block | What It Changes | Details |
|---|---|---|---|
system.enabled |
Runtime state / effective target | Enables or disables EMS output control. When disabled, effective targets become 0 and output writes are skipped. |
configuration.md, runtime-state.md |
system.dry_run |
Safety gates | Blocks real Zendure writes while still calculating targets and logging intended writes. The template value is false after placeholders are replaced; set true for manual no-write validation. |
configuration.md, safety.md |
system.allow_hardware_writes |
Safety gates | Allows Zendure /properties/write calls only when dry-run, simulation/replay, and placeholder safe-mode gates also allow them. The template value is true for normal live output control after real local values are configured. |
configuration.md, safety.md |
system.allow_state_reconciliation_writes |
SOC / mode reconciliation | Allows SOC, mode, runtime device state, and winter reconciliation writes after hardware writes are also allowed. The template value is true for the normal regulation profile after local limits have been reviewed and placeholders are replaced. |
configuration.md, safety.md |
system.max_total_power |
Total target calculation / limits | Caps the combined EMS target before allocation. Runtime state can override it. | configuration.md, runtime-state.md |
system.max_device_power |
Limits, clamping and redistribution | Default per-device output cap used when a device has no stronger configured cap. | configuration.md |
system.deadband |
Final write suppression | Skips small per-device outputLimit changes compared with current outputLimit, or current output when outputLimit is missing. |
control-logic.md |
system.min_output_limit |
Night / minSoc idle and effective target | Sets the enabled-control output floor, no-export standby total, and night/minSoc parking value. Runtime state can override it. | configuration.md, control-logic.md, runtime-state.md |
system.loop_interval |
Loop timing | Sets the time between EMS cycles, which also controls how often per-cycle ramps can step. Runtime state can override it. | configuration.md, runtime-state.md |
system.redistribute_clamped_power |
Limits, clamping and redistribution | Redistributes target watts that were clamped from one device to other devices with headroom. | configuration.md, control-logic.md |
system.pv_kwp_weighting |
PV-first weighting | Enables use of configured PV size in PV-first allocation weights. If disabled, devices[].pv_kwp is not applied to that weight. |
configuration.md, control-logic.md |
system.pv_charge_balance_enabled |
PV charge / SOC balancing | Enables the SOC-spread bias in PV-first allocation. | configuration.md, control-logic.md |
system.pv_charge_balance_deadband_percent |
PV charge / SOC balancing | Defines the SOC gap below which PV-first charge balancing stays inactive. | configuration.md, control-logic.md |
system.pv_charge_balance_full_bias_percent |
PV charge / SOC balancing | Defines the SOC gap where the configured balancing strength reaches full effect. | configuration.md, control-logic.md |
system.pv_charge_balance_strength |
PV charge / SOC balancing | Controls the maximum charge-balance multiplier applied to PV-first weights. | configuration.md, control-logic.md |
system.battery_kwh_weighting |
Battery top-up / battery weighting | Enables use of configured battery size in discharge and PV-first top-up weights. If disabled, usable SOC percent is used without kWh scaling. | configuration.md, control-logic.md |
system.soc_reconcile_interval |
SOC / mode reconciliation | Controls how often SOC/mode reconciliation is checked, measured in EMS cycles. 0 disables cyclic reconciliation. |
configuration.md, safety.md |
system.output_control.load_deadband_w |
Load filtering / total target stabilization | Holds the current total target when the filtered load is within this small-load band. | configuration.md, control-logic.md |
system.output_control.target_deadband_w |
Total target calculation | Holds the current commanded total when the newly desired total is only slightly different. This is earlier than final system.deadband. |
configuration.md, control-logic.md |
system.output_control.filter_enabled |
Load filtering | Enables or disables load filtering before total target calculation. | configuration.md |
system.output_control.filter_method |
Load filtering | Selects the filter method. The current tuned method is median_ema. |
configuration.md |
system.output_control.median_window |
Load filtering | Sets how many load samples are used by the median stage. | configuration.md |
system.output_control.ema_alpha |
Load filtering | Controls how quickly the EMA follows the median load value. Higher reacts faster. | configuration.md |
system.output_control.sign_change_fast_response_enabled |
Sign-change fast response | Lets the filter react faster when raw load has crossed zero but the smoothed value still points the other way. | configuration.md, control-logic.md |
system.output_control.sign_change_threshold_w |
Sign-change fast response | Sets the minimum import/export magnitude needed to trigger fast response. | configuration.md |
system.output_control.sign_change_filter_reset_factor |
Sign-change fast response | Sets how strongly the filtered load is pulled toward raw load during a sign-change mismatch. | configuration.md |
system.output_control.ramp_enabled |
Total ramp / bypass / stale telemetry | Enables per-cycle ramp limits for the combined target. | configuration.md, control-logic.md |
system.output_control.ramp_up_w_per_cycle |
Total ramp / bypass / stale telemetry | Limits how quickly the combined target can rise per EMS cycle. | configuration.md |
system.output_control.ramp_down_w_per_cycle |
Total ramp / bypass / stale telemetry | Limits how quickly the combined target can fall per EMS cycle. | configuration.md |
system.output_control.device_ramp_enabled |
Device ramp | Enables per-device target ramping after allocation. | configuration.md, control-logic.md |
system.output_control.device_ramp_up_w_per_cycle |
Device ramp | Limits how quickly each device target can rise per EMS cycle. | configuration.md |
system.output_control.device_ramp_down_w_per_cycle |
Device ramp | Limits how quickly each device target can fall per EMS cycle. | configuration.md |
system.output_control.large_import_bypass_w |
Total ramp / bypass | Detects large import situations. The controller increases ramp speed during bypass behavior. | configuration.md, control-logic.md |
system.output_control.large_export_bypass_w |
Total ramp / bypass | Detects large export situations. The controller increases ramp speed during bypass behavior. | configuration.md, control-logic.md |
system.output_control.bypass_ramp_multiplier |
Total ramp and device ramp | Multiplies total and per-device ramp limits during large import/export bypass situations. | configuration.md, control-logic.md |
system.output_control.telemetry_max_age_seconds |
Telemetry freshness / stale ramp | Marks device telemetry as stale after this age. Stale telemetry reduces ramp speed. | configuration.md, troubleshooting.md |
system.output_control.stale_telemetry_ramp_factor |
Total ramp / stale telemetry | Multiplies total ramp speed when telemetry is stale. Lower values make stale-telemetry changes more conservative. | configuration.md, troubleshooting.md |
devices[].max_power |
Limits, clamping and redistribution | Sets the per-device output cap used during allocation, final target clamping, and effective target calculation. Runtime state can override it. | configuration.md, runtime-state.md |
devices[].pv_kwp |
PV-first weighting | Represents the configured PV size for this device and scales the PV-first allocation weight when system.pv_kwp_weighting=true. |
configuration.md, control-logic.md |
devices[].pv_priority_factor |
PV-first weighting | Amplifies or reduces this device's PV-first allocation weight. Runtime state can override it. It does not create real PV power. | configuration.md, runtime-state.md, control-logic.md |
devices[].battery_kwh |
Battery top-up / battery weighting | Represents configured battery size and scales discharge/top-up allocation when system.battery_kwh_weighting=true. |
configuration.md, control-logic.md |
devices[].min_soc |
SOC reconciliation / battery weighting / idle protection | Sets the discharge floor used for SOC reconciliation, usable battery weighting, top-up eligibility, and night/minSoc idle detection. | configuration.md, control-logic.md |
devices[].max_soc |
SOC reconciliation | Sets the upper SOC boundary used by reconciliation and remaining-time context. | configuration.md, safety.md |
Measurements are real runtime inputs: Shelly import/export, Zendure PV
telemetry, SOC, current output, current outputLimit, pack power, and detected
capability state. Config values do not replace these measurements; they shape
how the EMS reacts to them.
Filters and smoothing reduce noise or slow changes. load_deadband_w suppresses
small filtered load changes before the total target moves. target_deadband_w
suppresses small total-target changes against the controller's current
commanded total. Ramps limit how far the total target or device targets can move
per cycle.
Weighting values influence distribution between devices, but do not create additional PV or battery power. PV-first weighting uses this simplified signal:
pv_weight = pv_only * pv_kwp * pv_priority_factor * charge_balance_multiplier
If system.pv_kwp_weighting=false, the configured pv_kwp size is not applied
to the PV-first weight. devices[].pv_priority_factor still acts only as an
allocation amplifier inside the available PV-only limit.
Battery weighting uses this simplified signal:
usable_percent = max(0, soc - min_soc)
battery_weight = battery_kwh * usable_percent / 100
If system.battery_kwh_weighting=false, the configured battery_kwh size is
not applied and the EMS weights discharge by usable SOC percent instead.
Limits and clamps constrain output. system.max_total_power limits the combined
target. devices[].max_power and the default system.max_device_power limit
individual devices. system.min_output_limit can raise an enabled device target
to the configured floor. devices[].min_soc and devices[].max_soc constrain
SOC reconciliation and battery eligibility.
Write suppression and write safety happen near the end of the cycle.
system.deadband is the final per-device write suppression threshold and is
separate from system.output_control.target_deadband_w. Real Zendure writes
also require the safety gates described in safety.md: no dry-run,
no simulation/replay, and system.allow_hardware_writes=true. State
reconciliation writes additionally require
system.allow_state_reconciliation_writes=true.