Demand Response · Tibber · Nordpool · Dynamic Tariff · KNX · 10 min read

Tibber and Nordpool Dynamic Tariff Integration with KNX

Connecting Tibber and Nordpool real-time electricity prices to KNX allows the building automation layer to automatically shift flexible loads — heat pump, EV charger, dishwasher, battery charging — into cheap price windows, reducing electricity costs without sacrificing occupant comfort.

Tibber API: GraphQL endpoint and price query

Tibber provides real-time and forecast electricity prices through a GraphQL API at the endpoint https://api.tibber.com/v1-beta/gql. Authentication uses a Bearer token issued from the Tibber developer portal (developer.tibber.com). The token is account-specific and does not expire unless revoked.

The most useful query for KNX integration retrieves the current hour price and the CHEAP/NORMAL/EXPENSIVE classification that Tibber calculates based on the spot price distribution over the next 24 hours. This classification is ready-made for direct mapping to a KNX tariff level group address without additional threshold calculation.

Tibber GraphQL query — current price and level

POST https://api.tibber.com/v1-beta/gql
Authorization: Bearer <your-tibber-token>
Content-Type: application/json

{
  "query": "{ viewer { homes { currentSubscription { priceInfo { current { total level } } } } } }"
}

Response:
{
  "data": {
    "viewer": {
      "homes": [{
        "currentSubscription": {
          "priceInfo": {
            "current": {
              "total": 0.1842,
              "level": "CHEAP"
            }
          }
        }
      }]
    }
  }
}

Possible level values: VERY_CHEAP, CHEAP, NORMAL, EXPENSIVE, VERY_EXPENSIVE

The total field gives the all-in price in local currency per kWh including grid fees, taxes, and Tibber markup. The level field is the classification to use for KNX logic — it removes the need for manual threshold calibration per region. Polling frequency: once per hour is sufficient since the price changes on the hour. Add a 2-minute delay after the hour (query at HH:02) to ensure Tibber has published the new price before the KNX system reads it.

Python bridge on Raspberry Pi: polling and KNX write

A Python script running on a Raspberry Pi (any model with Ethernet) acts as the bridge between the Tibber API and the KNX bus. The script uses the knxpy library to write group telegrams via a KNX IP Interface (e.g. MDT SCN-IP100.02 or Weinzierl KNX IP Interface 731) or via a knxd daemon on the same Pi connected to a KNX USB interface.

Python bridge — Tibber to KNX DPT 5.010

#!/usr/bin/env python3
import requests, time, schedule
import knxpy

TIBBER_TOKEN = "your-tibber-token"
KNX_IP = "192.168.1.50"   # KNX IP Interface address
TARIFF_GA = "5/0/1"       # DPT 5.010 — tariff level GA

LEVEL_MAP = {
    "VERY_CHEAP":  1,
    "CHEAP":       1,
    "NORMAL":      2,
    "EXPENSIVE":   3,
    "VERY_EXPENSIVE": 3,
}

def get_tibber_level():
    query = '{"query":"{ viewer { homes { currentSubscription { priceInfo { current { level } } } } } }"}'
    r = requests.post(
        "https://api.tibber.com/v1-beta/gql",
        headers={"Authorization": f"Bearer {TIBBER_TOKEN}", "Content-Type": "application/json"},
        data=query, timeout=10
    )
    level = r.json()["data"]["viewer"]["homes"][0]["currentSubscription"]["priceInfo"]["current"]["level"]
    return LEVEL_MAP.get(level, 2)

def update_knx_tariff():
    level = get_tibber_level()
    knx = knxpy.KNXIPTunnel(KNX_IP, 3671)
    knx.connect()
    knx.group_write(TARIFF_GA, [level], dpt="DPT_Value_1_Ucount")
    knx.close()
    print(f"KNX tariff level written: {level}")

schedule.every().hour.at(":02").do(update_knx_tariff)
update_knx_tariff()  # run on startup
while True:
    schedule.run_pending()
    time.sleep(30)

The DPT 5.010 (unsigned byte, 8-bit count) encodes the tariff level as a plain integer: 1 for CHEAP, 2 for NORMAL, 3 for EXPENSIVE. This maps cleanly to KNX logic block comparator inputs without scaling. Run the script as a systemd service to ensure it restarts on Pi reboot.

KNX logic blocks: load control based on tariff level

MDT SCN-LCRM.01 logic controller receives the DPT 5.010 tariff level on a KNX GA and uses comparator blocks to derive binary control signals for each load. Three comparators compare the received byte against thresholds 1, 2, and 3, generating enable/disable telegrams for configured group addresses.

Tariff LevelActionGroup AddressDPT / Value
1 (CHEAP)Enable dishwasher / washing machine socket3/1/1DPT 1.001 / ON
1 (CHEAP)SG Ready State 3 — HP increase setpoint3/1/2 + 3/1/3DPT 1.001 / OFF + ON
1 (CHEAP)Battery forced charge via Modbus write3/1/4DPT 1.001 / ON
2 (NORMAL)All optional loads normal operationNo change
3 (EXPENSIVE)Disable dishwasher / washing machine socket3/1/1DPT 1.001 / OFF
3 (EXPENSIVE)Shed EV charging (write 0A to charger GA)5/3/1DPT 5.010 / 0
3 (EXPENSIVE)Battery forced discharge3/1/4DPT 1.001 / OFF

SG Ready State 3 wiring: the MDT logic sends DPT 1.001 OFF to S1 relay GA and DPT 1.001 ON to S2 relay GA simultaneously. The MDT AKD-0800.01 relay actuator converts these KNX telegrams to 230V relay contact closure on the heat pump SG Ready terminals. State 3 typically raises the DHW setpoint by 3-5K and increases space heating setpoint by 2K.

Nordpool spot price integration: day-ahead schedule

Nordpool publishes day-ahead spot prices at approximately 13:00 CET each day for the following 24 hours. Unlike Tibber (which requires a subscription and smart meter), Nordpool prices are available via the Nordpool public API without authentication for most bidding zones, or via the ENTSO-E Transparency Platform API (requires free registration at transparency.entsoe.eu).

Nordpool day-ahead price script — next-day KNX schedule

# Runs at 13:30 daily — downloads next-day prices and creates KNX schedule
import requests, json
from datetime import date, timedelta

BIDDING_ZONE = "SE3"   # Sweden zone 3 — change per country
CURRENCY = "SEK"

def fetch_nordpool_prices():
    tomorrow = (date.today() + timedelta(days=1)).strftime("%Y-%m-%d")
    url = f"https://dataportal-api.nordpoolgroup.com/api/DayAheadPrices?market=N2EX_DayAhead&deliveryArea={BIDDING_ZONE}&currency={CURRENCY}&date={tomorrow}"
    r = requests.get(url, timeout=15)
    return r.json()["multiAreaEntries"]

def classify_price(price_eur, prices_list):
    sorted_prices = sorted(prices_list)
    p33 = sorted_prices[len(sorted_prices) // 3]
    p66 = sorted_prices[2 * len(sorted_prices) // 3]
    if price_eur <= p33:
        return 1  # CHEAP
    elif price_eur <= p66:
        return 2  # NORMAL
    else:
        return 3  # EXPENSIVE

# Output: list of 24 (hour, level) tuples written to KNX time-scheduled GA
# Use MDT SCN-LCRM.01 weekly timer or Home Assistant KNX time schedule

The classification thresholds (33rd and 66th percentile of tomorrow's 24 prices) ensure that approximately 8 hours are classified as CHEAP, 8 as NORMAL, and 8 as EXPENSIVE each day — balanced regardless of absolute price level. This is preferable to fixed EUR/kWh thresholds that may become stale as energy markets evolve. The resulting 24-entry schedule is written to a KNX time program on the MDT logic controller, replacing the current-hour Tibber poll for installations without a Tibber contract.

KNX touchpanel price visualization and payback

The current tariff level is displayed on a KNX touchpanel (Gira X1 or MDT Glastaster II) as a colour-coded indicator: green background for CHEAP (level 1), amber for NORMAL (level 2), red for EXPENSIVE (level 3). A KNX scene object linked to the tariff level GA (5/0/1) changes the panel background colour via a scene write. The panel also shows the current spot price as a DPT 9.002 value in EUR/kWh derived from the Tibber API total field, allowing occupants to make manual decisions about flexible loads.

Payback calculation for Nordic markets: a heat pump consuming 10 kWh/day for space heating shifted from expensive to cheap hours (price spread typically 0.05-0.15 EUR/kWh in SE3/FI) saves 0.50-1.50 EUR/day, or 180-550 EUR/year. The Raspberry Pi, KNX IP Interface, and MDT logic controller total approximately 300-500 EUR in hardware — payback in 1-2 years. In high-spread markets such as Denmark (DK1/DK2) and Germany (DE-LU) with spreads exceeding 0.20 EUR/kWh, payback can be under 12 months.

Tibber vs Nordpool API choice

Use Tibber when the building already has a Tibber electricity contract — the CHEAP/NORMAL/EXPENSIVE classification is pre-calculated and includes grid fee components. Use Nordpool when the building has a different supplier but wants spot-price following — requires manual threshold classification per the percentile method above.

ENTSO-E as Nordpool alternative

The ENTSO-E Transparency Platform API (transparency.entsoe.eu/api) provides day-ahead prices for all European bidding zones. Register for a free API token. Query the Day Ahead Prices endpoint (documentType=A44, in_Domain and out_Domain using EIC area codes). Useful for countries outside Nordpool coverage such as Spain, Italy, and Poland.

Need a KNX panel with dynamic tariff integration and HEMS logic built to spec?

We design low-voltage panels with Tibber/Nordpool API bridges, MDT logic controller programming, SG Ready relay outputs, and full commissioning documentation — delivered tested to your site.

Request a quote →
Loading...
Back to top