Battery SOC · KNX Automation · Load Shedding · Tariff Scheduling · 10 min read

Battery SOC KNX Automation: Load Shedding, Overnight Charging and Tariff Scheduling

Battery state of charge is the most important input variable for KNX energy automation — it determines when to allow additional loads, when to shed loads to protect the battery, when to trigger overnight off-peak charging, and how to adapt strategy between summer and winter operating conditions. A well-designed SOC automation layer can reduce grid import costs by 40–70% compared to basic self-consumption control.

SOC threshold table with hysteresis bands

The SOC threshold table defines discrete operating bands, each triggering a specific set of KNX actions. Hysteresis — a recovery offset above the threshold — prevents rapid cycling when SOC hovers near a threshold boundary due to load fluctuations or noisy SOC measurement.

SOC bandKNX actions allowedHysteresis recovery
> 90% AND exportingSG Ready State 4 + EV max current + immersion heater ONActive until SOC drops to 85% or stops exporting
50–90%Normal self-consumption: SG Ready State 3 + EV priority modeRemain in band — no shedding
20–50%Basic self-consumption only — no SG Ready State 4, EV at 6A minRecover to 50-90% band when solar charges battery above 55%
< 20%Suspend all controllable loads: EV disabled, SG Ready → State 2Resume loads when SOC recovers above 25% (5% hysteresis)
< 10%Emergency: HVAC setback −2°C + load alert to KNX visualisationCancel emergency setback when SOC recovers above 15%

MDT Logic Module — SOC bands with hysteresis implementation

Logic channel 1: SOC band flags (set on GA 9/1/0 change)
  Flag "soc_critical": SOC < 20 → set; clear when SOC > 25
  Flag "soc_low":      SOC < 10 → set; clear when SOC > 15
  Flag "soc_full":     SOC > 90 → set; clear when SOC < 85
  On-delay timer: 60s before setting any flag (noise rejection)

Logic channel 2: Load enable gate
  IF soc_critical: disable EV (write 0 to GA 10/0/1)
                   set SG Ready → State 2 (S1=0, S2=0)
  IF NOT soc_critical AND surplus > 2500W:
    EV: enable + set current from surplus/230
    SG Ready: State 3 if surplus > 3000W

Logic channel 3: Emergency HVAC setback
  IF soc_low: send HVAC setback scene (reduce setpoint −2°C)
              write 1 to alarm GA 9/9/0 (battery emergency alert)
  IF NOT soc_low AND NOT soc_critical: restore HVAC setpoint

Off-peak overnight charging trigger

Dynamic electricity tariffs (Tibber, Nordpool, Octopus Agile) offer periods where grid electricity costs less than 0.10 EUR/kWh — typically between 00:00 and 06:00. Triggering battery charging during these windows reduces daily energy costs when solar generation is insufficient to fully charge the battery during the day.

Off-peak overnight charging logic

KNX time-based trigger (IP-Symcon or HA schedule):
  Trigger time: 01:00 local time
  Condition: GA 9/1/0 (Battery SOC) < 40%
             AND KNX tariff GA 9/9/1 < 10 (price < 0.10 €/kWh)

Action: Set MultiPlus to forced battery charge mode
  Victron (via Cerbo GX Modbus write):
    Write 4 to register 37 (unit ID 227) → force bulk charge
  Fronius GEN24 (via webserver API or EVCC):
    evcc.yaml: scheduled charging at 01:00 with "now" mode

KNX tariff GA update (source: HA automation reading Tibber API):
  HA script (runs at 22:00 daily):
    GET https://api.tibber.com/v1-beta/gql (Tibber price API)
    Find minimum price in next 8 hours
    Write min_price × 100 (integer cents) to GA 9/9/1

  GA 9/9/1 = current electricity price in euro cents (DPT 9.002)
  GA 9/9/2 = off-peak start hour (DPT 5.010)

Cancel condition: 06:00 OR GA 9/1/0 > 90%
  → Stop forced charge → return to normal ESS mode

Nordpool vs Tibber for KNX integration: Nordpool publishes day-ahead prices at 13:00 CET for the following day — suitable for HA automations that fetch prices once daily and schedule charging accordingly. Tibber provides a higher-level API with hourly price forecasts and supports WebSocket push subscriptions for real-time price updates, enabling dynamic threshold adjustments in the KNX tariff GA without polling.

Morning pre-charge before peak tariff

Time-of-use tariffs often have peak pricing windows from 07:00 to 11:00 when morning building load is high but solar generation is still building. Pre-charging the battery to at least 50% SOC before the peak window using cheaper off-peak energy ensures the building draws from battery rather than the expensive grid during peak hours.

Morning pre-charge automation (IP-Symcon script)

// IP-Symcon PHP script — runs at 06:00 daily
$soc = GetValueFloat(IPS_GetVariableIDByName("Battery SOC", $parentID));
$pvForecast = GetValueFloat($pvForecastVariableID); // from PV forecast API

// PV forecast integration: HA reads forecast.solar API
// GET https://api.forecast.solar/estimate/<lat>/<lon>/<dec>/<az>/<kwp>
// Forecast for today: energy in kWh — high forecast means skip pre-charge

if ($soc < 30 && $pvForecast < 15) {
  // Low SOC and low solar forecast — pre-charge to 50% before peak
  SetValueInteger($multiPlusMode, 4); // Force bulk charge
  IPS_RunScriptEx($chargeStopScript, ["targetSOC" => 50]);
}

// In HA automation:
automation:
  - alias: "Morning pre-charge if low SOC"
    trigger:
      - platform: time
        at: "06:00:00"
    condition:
      - condition: numeric_state
        entity_id: sensor.battery_soc
        below: 30
      - condition: template
        value_template: "{{ states('sensor.pv_forecast_today_kwh')|float < 15 }}"
    action:
      - service: script.force_battery_charge_to_50_percent

Seasonal SOC strategy

Battery operating strategy should adapt to seasonal solar availability. In summer, the battery cycles daily between near-empty and near-full — deep daily cycling maximises self-consumption but requires good battery chemistry (LFP preferred over NMC for daily deep cycling longevity). In winter, lower generation means the battery may rarely fully charge, so it should be kept at a mid-range SOC to preserve backup capacity.

SeasonTarget SOC rangeStrategyKNX schedule trigger
Summer (April–Sept)20–95%Deep daily cycling — max self-consumption. Allow EV + HP loads when SOC > 80%.Apr 1 + Oct 1 seasonal mode switch
Winter (Oct–Mar)40–80%Maintain mid-range SOC for backup. Pre-charge to 80% before morning peak. No deep discharge.Oct 1 mode switch to winter strategy
Long cloudy periods60–80%Suspend deep cycling — grid import at off-peak preferred over battery below 60%.Automatic: if SOC < 60% at 09:00 for 3+ days
Heatwave (>35°C)20–80%Reduce upper limit to protect battery temperature (BMS thermal derating)If GA 9/1/2 (battery temp) > 35°C

Seasonal mode switch via KNX time schedule

KNX time schedule (Gira X1 or MDT Logic Module timer):

April 1 — summer mode:
  Write "summer" flag → GA 9/9/5 value 1
  SOC shedding threshold: 20% (allow deep cycling)
  EV enable threshold SOC: 40% (lower than winter)
  Morning pre-charge: disabled (sufficient solar expected)

October 1 — winter mode:
  Write "winter" flag → GA 9/9/5 value 0
  SOC shedding threshold: 40% (protect from deep discharge)
  EV enable threshold SOC: 60% (higher — conserve battery)
  Morning pre-charge: enabled (low solar, pre-charge from grid)

IP-Symcon monthly schedule (runs on 1st of each month):
  $month = date("n"); // PHP month number
  if ($month >= 4 && $month <= 9) {
    SetValue($seasonMode, "summer");
  } else {
    SetValue($seasonMode, "winter");
  }

KNX energy tariff GA: Tibber and Nordpool via HA

For dynamic tariff-aware battery scheduling, the current electricity price must be published to a KNX group address where the KNX logic module can compare it against threshold values. Home Assistant or IP-Symcon reads the tariff API and writes the price to a KNX GA on each hourly update.

HA Tibber → KNX tariff GA automation

# HA configuration.yaml — Tibber integration
tibber:
  access_token: !secret tibber_access_token

# sensor: current electricity price in EUR/kWh
sensor:
  - platform: tibber
    name: tibber_current_price

# automation: write current price to KNX GA every hour
automation:
  - alias: "Tibber price → KNX tariff GA"
    trigger:
      - platform: time_pattern
        minutes: "0"    # Run at :00 of every hour
    action:
      - service: knx.send
        data:
          address: "9/9/1"
          payload: >
            {{ (states('sensor.tibber_current_price')|float * 1000)|int }}
          # Scale: multiply by 1000 to get milli-EUR (DPT 9.002 compatible)
          type: "2byte_float"

# KNX logic uses GA 9/9/1 value:
#   < 100 (0.10 EUR/kWh): trigger off-peak battery charge
#   > 300 (0.30 EUR/kWh): enable all self-consumption loads aggressively
#   > 500 (0.50 EUR/kWh): emergency: maximise battery discharge, no grid import

Commissioning with real SOC test scenarios

SOC automation commissioning requires testing each threshold band by injecting simulated SOC values via ETS6 Group Monitor — without waiting for the battery to naturally cycle through each state. Test each threshold in sequence and verify the correct KNX actions fire and cease at the right SOC values including hysteresis.

SOC automation commissioning test scenarios

Prerequisite: disconnect real battery SOC input to KNX
  (remove Intesis IN701KNX poll for register 843 temporarily)
  → manually write SOC test values to GA 9/1/0 via ETS6

Test 1 — Normal operation (SOC 75%):
  Write 75 to GA 9/1/0
  Expected: no load shedding, self-consumption logic active
  Verify: EV and SG Ready loads enabled (if surplus present)

Test 2 — Enter low SOC zone (SOC 18%):
  Write 18 to GA 9/1/0
  Expected: after 60s on-delay, soc_critical flag sets
  Verify: GA 10/0/1 (EV enable) → 0 (EV disabled)
          SG Ready relays → State 2 (both contacts open)

Test 3 — Hysteresis: recovery insufficient (SOC 22%):
  Write 22 to GA 9/1/0 (above 20% threshold but below 25% recovery)
  Expected: soc_critical flag remains set (hysteresis holds)
  Verify: loads remain disabled — confirm hysteresis working

Test 4 — Hysteresis: recovery sufficient (SOC 27%):
  Write 27 to GA 9/1/0 (above 25% recovery threshold)
  Expected: soc_critical flag clears after 60s
  Verify: loads re-enable (if surplus still present)

Test 5 — Emergency zone (SOC 8%):
  Write 8 to GA 9/1/0
  Expected: HVAC setback scene activates (HVAC −2°C)
            Alarm GA 9/9/0 → 1 (alert fires in Gira X1)
  Verify in HVAC setpoint display: temperature reduced by 2°C

Test 6 — Off-peak charging trigger:
  Write 35 to GA 9/1/0 (SOC 35% — below 40% threshold)
  Set time to 01:05 (test time via IP-Symcon or HA test trigger)
  Write 8 to GA 9/9/1 (price 0.008 EUR/kWh — very cheap)
  Expected: forced battery charge mode activates
  Verify: MultiPlus/GEN24 starts charging from grid

Reconnect live SOC after testing

After completing simulated SOC tests, re-enable the Intesis poll for the battery SOC register. Verify the live SOC value matches the battery BMS display. Confirm the logic transitions correctly with live SOC data before handing over the system.

Seasonal mode verification

Test seasonal mode switch by temporarily writing summer and winter flags to GA 9/9/5. Verify that SOC thresholds change as expected: the summer EV enable threshold (40% SOC) should be lower than the winter threshold (60% SOC). Restore to correct seasonal mode after testing.

Need SOC-based KNX energy automation designed and commissioned?

We design and configure SOC threshold automation, off-peak charging schedules, seasonal strategy logic, and Tibber/Nordpool tariff integration for KNX buildings with battery storage — fully tested and documented.

Request a quote →
Loading...
Back to top