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_EXPENSIVEThe 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 Level | Action | Group Address | DPT / Value |
|---|---|---|---|
| 1 (CHEAP) | Enable dishwasher / washing machine socket | 3/1/1 | DPT 1.001 / ON |
| 1 (CHEAP) | SG Ready State 3 — HP increase setpoint | 3/1/2 + 3/1/3 | DPT 1.001 / OFF + ON |
| 1 (CHEAP) | Battery forced charge via Modbus write | 3/1/4 | DPT 1.001 / ON |
| 2 (NORMAL) | All optional loads normal operation | — | No change |
| 3 (EXPENSIVE) | Disable dishwasher / washing machine socket | 3/1/1 | DPT 1.001 / OFF |
| 3 (EXPENSIVE) | Shed EV charging (write 0A to charger GA) | 5/3/1 | DPT 5.010 / 0 |
| 3 (EXPENSIVE) | Battery forced discharge | 3/1/4 | DPT 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}¤cy={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 scheduleThe 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 →