TapHome

OTE-CR Spot Prices

Packet Parser → HTTP
Submitted by
Last updated: 03. 2026

OTE-CR (Operátor trhu s elektřinou) is the Czech electricity and gas market operator. The TapHome template fetches Day-Ahead Market spot prices from the public OTE-CR API and exposes them as devices — current price, daily statistics, ranking, and time-window variants. Prices are published in EUR per MWh and the template includes a built-in currency conversion variable for displaying values in CZK.

The template polls the OTE-CR API once per hour and retrieves up to 96 hourly price periods (4 days of data). No authentication is required — the API endpoint is public.

Configuration

During template import, TapHome connects to www.ote-cr.cz over HTTPS (port 443). No IP address or credentials are needed — the endpoint is fixed.

Two optional import parameters control the time-window devices:

  • From — start hour (0–23, default: 9) for the time window filter
  • To — end hour (0–23, default: 17) for the time window filter

The defaults (9–17) cover standard working hours. Adjust to match your preferred analysis window.

After import, the Currency Exchange Rate (kurz) variable defaults to 1 — meaning all price devices display raw EUR/MWh values. Set it to the current EUR/CZK rate (e.g. 25.2) so that all price devices output values in CZK. You can set it manually in the device detail, or link it to the CNB exchange rate template for automatic daily updates (see below).

Device capabilities

Current price

The Current Price device reads the spot price for the current hour. The template determines the active period from the system clock and multiplies the EUR value by the exchange rate variable to output the price in CZK.

Price ranking
  • Current Period Rank — the rank of the current hour’s price among all periods of the day (1 = cheapest, 24 = most expensive)
  • Current Period Rank in Time Window — the same ranking but limited to the user-defined time window (From–To). Returns NaN if the current hour falls outside the window.
Daily price statistics
  • Highest Price of Day — maximum spot price across all periods, converted to CZK
  • Lowest Price of Day — minimum spot price across all periods, converted to CZK
  • Average Price of Day — arithmetic mean of all periods, converted to CZK
Time-window price statistics

Each daily statistic has a windowed counterpart that filters periods to the From–To range:

  • Highest Price in Time Window
  • Lowest Price in Time Window
  • Average Price in Time Window

All windowed devices return NaN if From >= To.

Period navigation
  • Cheapest Period of Day — the 1-based period number of the cheapest hour in the day
  • Cheapest Remaining Period — the cheapest hour from the current time onward
  • Period Count — total number of available price periods (typically 96)
  • Period Count in Time Window — number of periods within the From–To range
Currency Exchange Rate

The Currency Exchange Rate device is the only read-write device in the template. It stores the EUR to CZK conversion rate used by all price devices. Set it manually or link it to the CNB exchange rate template for automatic daily updates.

When the exchange rate is set to 1, all price devices output raw EUR values.

Service attributes

The template exposes 96 service attributes (Period 1 through Period 96), each showing the raw spot price in EUR for the corresponding hourly slot. These cover approximately 4 days of Day-Ahead Market data and are useful for diagnostics or for building custom automations that inspect specific future periods.

Linking with CNB exchange rate

To automate the EUR to CZK conversion:

  1. Import the CNB exchange rate template (Kurzy ČNB) into TapHome
  2. In the OTE-CR template, create a formula that reads the EUR/CZK rate from the CNB device and writes it to the Currency Exchange Rate device
  3. The CNB template updates the rate daily, so all OTE-CR prices automatically reflect the current exchange rate

Automation examples

Spot prices are most useful when combined with TapHome Smart Rules:

  • Heat water during cheapest hours — compare the current period rank against a threshold (e.g. rank <= 4) and turn on the water heater only during the 4 cheapest hours of the day
  • Charge batteries at off-peak — use the Current Price device to trigger battery charging when the price drops below a configurable limit
  • Shift appliance load — use the Cheapest Remaining Period device to schedule washing machines, dishwashers, or EV charging for the next cheapest slot
  • Time-window optimization — restrict automation to specific hours (e.g. 6:00–22:00) using the windowed devices, so overnight periods are excluded from ranking

Troubleshooting

All prices show zero or NaN
  1. Verify that TapHome CCU has internet access (the template connects to www.ote-cr.cz over HTTPS)
  2. Check the Period Count device — if it shows 0, the API response is empty or parsing failed
  3. Ensure the Currency Exchange Rate device is set to a non-zero value — a rate of 0 will multiply all prices to zero
Time-window devices return NaN
  1. Verify the From and To import parameters are set correctly (From must be less than To)
  2. If the current hour is outside the From–To range, the rank-in-window device returns NaN by design
Prices seem outdated

The template polls the OTE-CR API every hour (3600 seconds). Day-Ahead Market prices are published by OTE the day before, typically around 12:45 CET. If the new day’s prices are not yet visible, wait until after the publication time.

Available devices

OTE-CR Spot Prices Module
Service Attributes
Period 1Spot price for hour 1 (EUR)
Period 2Spot price for hour 2 (EUR)
Period 3Spot price for hour 3 (EUR)
Period 4Spot price for hour 4 (EUR)
Period 5Spot price for hour 5 (EUR)
Period 6Spot price for hour 6 (EUR)
Period 7Spot price for hour 7 (EUR)
Period 8Spot price for hour 8 (EUR)
Period 9Spot price for hour 9 (EUR)
Period 10Spot price for hour 10 (EUR)
Period 11Spot price for hour 11 (EUR)
Period 12Spot price for hour 12 (EUR)
Period 13Spot price for hour 13 (EUR)
Period 14Spot price for hour 14 (EUR)
Period 15Spot price for hour 15 (EUR)
Period 16Spot price for hour 16 (EUR)
Period 17Spot price for hour 17 (EUR)
Period 18Spot price for hour 18 (EUR)
Period 19Spot price for hour 19 (EUR)
Period 20Spot price for hour 20 (EUR)
Period 21Spot price for hour 21 (EUR)
Period 22Spot price for hour 22 (EUR)
Period 23Spot price for hour 23 (EUR)
Period 24Spot price for hour 24 (EUR)
Period 25
Period 26
Period 27
Period 28
Period 29
Period 30
Period 31
Period 32
Period 33
Period 34
Period 35
Period 36
Period 37
Period 38
Period 39
Period 40
Period 41
Period 42
Period 43
Period 44
Period 45
Period 46
Period 47
Period 48
Period 49
Period 50
Period 51
Period 52
Period 53
Period 54
Period 55
Period 56
Period 57
Period 58
Period 59
Period 60
Period 61
Period 62
Period 63
Period 64
Period 65
Period 66
Period 67
Period 68
Period 69
Period 70
Period 71
Period 72
Period 73
Period 74
Period 75
Period 76
Period 77
Period 78
Period 79
Period 80
Period 81
Period 82
Period 83
Period 84
Period 85
Period 86
Period 87
Period 88
Period 89
Period 90
Period 91
Period 92
Period 93
Period 94
Period 95
Period 96
Custom Variables
From (numeric) = 9Start hour (0–23) of the time window for windowed price ranking and statistics
To (numeric) = 17End hour (0–23) of the time window for windowed price ranking and statistics
kurz (numeric) = 1EUR to CZK exchange rate multiplier — all prices are multiplied by this value (set to current rate, e.g. 25.2)

ote-cr

Read (module)
VAR today := tostring(NOW(), "yyyy-MM-dd");
VAR response := SENDHTTPREQUEST("/cs/kratkodobe-trhy/elektrina/denni-trh/chart-data");
IF response.IsSuccess
    output := response.Content;
ELSE
    ADDERROR(response.StatusCode + " (" + response.ReasonPhrase + ")");
END
Service Attributes
Perioda 1
PARSEJSON(output, "$.data.dataLine[1].point[0].y") + " EUR"
Perioda 2
PARSEJSON(output, "$.data.dataLine[1].point[1].y") + " EUR"
Perioda 3
PARSEJSON(output, "$.data.dataLine[1].point[2].y") + " EUR"
Perioda 4
PARSEJSON(output, "$.data.dataLine[1].point[3].y") + " EUR"
Perioda 5
PARSEJSON(output, "$.data.dataLine[1].point[4].y") + " EUR"
Perioda 6
PARSEJSON(output, "$.data.dataLine[1].point[5].y") + " EUR"
Perioda 7
PARSEJSON(output, "$.data.dataLine[1].point[6].y") + " EUR"
Perioda 8
PARSEJSON(output, "$.data.dataLine[1].point[7].y") + " EUR"
Perioda 9
PARSEJSON(output, "$.data.dataLine[1].point[8].y") + " EUR"
Perioda 10
PARSEJSON(output, "$.data.dataLine[1].point[9].y") + " EUR"
Perioda 11
PARSEJSON(output, "$.data.dataLine[1].point[10].y") + " EUR"
Perioda 12
PARSEJSON(output, "$.data.dataLine[1].point[11].y") + " EUR"
Perioda 13
PARSEJSON(output, "$.data.dataLine[1].point[12].y") + " EUR"
Perioda 14
PARSEJSON(output, "$.data.dataLine[1].point[13].y") + " EUR"
Perioda 15
PARSEJSON(output, "$.data.dataLine[1].point[14].y") + " EUR"
Perioda 16
PARSEJSON(output, "$.data.dataLine[1].point[15].y") + " EUR"
Perioda 17
PARSEJSON(output, "$.data.dataLine[1].point[16].y") + " EUR"
Perioda 18
PARSEJSON(output, "$.data.dataLine[1].point[17].y") + " EUR"
Perioda 19
PARSEJSON(output, "$.data.dataLine[1].point[18].y") + " EUR"
Perioda 20
PARSEJSON(output, "$.data.dataLine[1].point[19].y") + " EUR"
Perioda 21
PARSEJSON(output, "$.data.dataLine[1].point[20].y") + " EUR"
Perioda 22
PARSEJSON(output, "$.data.dataLine[1].point[21].y") + " EUR"
Perioda 23
PARSEJSON(output, "$.data.dataLine[1].point[22].y") + " EUR"
Perioda 24
PARSEJSON(output, "$.data.dataLine[1].point[23].y") + " EUR"
Perioda 25
PARSEJSON(output, "$.data.dataLine[1].point[24].y") + " EUR"
Perioda 26
PARSEJSON(output, "$.data.dataLine[1].point[25].y") + " EUR"
Perioda 27
PARSEJSON(output, "$.data.dataLine[1].point[26].y") + " EUR"
Perioda 28
PARSEJSON(output, "$.data.dataLine[1].point[27].y") + " EUR"
Perioda 29
PARSEJSON(output, "$.data.dataLine[1].point[28].y") + " EUR"
Perioda 30
PARSEJSON(output, "$.data.dataLine[1].point[29].y") + " EUR"
Perioda 31
PARSEJSON(output, "$.data.dataLine[1].point[30].y") + " EUR"
Perioda 32
PARSEJSON(output, "$.data.dataLine[1].point[31].y") + " EUR"
Perioda 33
PARSEJSON(output, "$.data.dataLine[1].point[32].y") + " EUR"
Perioda 34
PARSEJSON(output, "$.data.dataLine[1].point[33].y") + " EUR"
Perioda 35
PARSEJSON(output, "$.data.dataLine[1].point[34].y") + " EUR"
Perioda 36
PARSEJSON(output, "$.data.dataLine[1].point[35].y") + " EUR"
Perioda 37
PARSEJSON(output, "$.data.dataLine[1].point[36].y") + " EUR"
Perioda 38
PARSEJSON(output, "$.data.dataLine[1].point[37].y") + " EUR"
Perioda 39
PARSEJSON(output, "$.data.dataLine[1].point[38].y") + " EUR"
Perioda 40
PARSEJSON(output, "$.data.dataLine[1].point[39].y") + " EUR"
Perioda 41
PARSEJSON(output, "$.data.dataLine[1].point[40].y") + " EUR"
Perioda 42
PARSEJSON(output, "$.data.dataLine[1].point[41].y") + " EUR"
Perioda 43
PARSEJSON(output, "$.data.dataLine[1].point[42].y") + " EUR"
Perioda 44
PARSEJSON(output, "$.data.dataLine[1].point[43].y") + " EUR"
Perioda 45
PARSEJSON(output, "$.data.dataLine[1].point[44].y") + " EUR"
Perioda 46
PARSEJSON(output, "$.data.dataLine[1].point[45].y") + " EUR"
Perioda 47
PARSEJSON(output, "$.data.dataLine[1].point[46].y") + " EUR"
Perioda 48
PARSEJSON(output, "$.data.dataLine[1].point[47].y") + " EUR"
Perioda 49
PARSEJSON(output, "$.data.dataLine[1].point[48].y") + " EUR"
Perioda 50
PARSEJSON(output, "$.data.dataLine[1].point[49].y") + " EUR"
Perioda 51
PARSEJSON(output, "$.data.dataLine[1].point[50].y") + " EUR"
Perioda 52
PARSEJSON(output, "$.data.dataLine[1].point[51].y") + " EUR"
Perioda 53
PARSEJSON(output, "$.data.dataLine[1].point[52].y") + " EUR"
Perioda 54
PARSEJSON(output, "$.data.dataLine[1].point[53].y") + " EUR"
Perioda 55
PARSEJSON(output, "$.data.dataLine[1].point[54].y") + " EUR"
Perioda 56
PARSEJSON(output, "$.data.dataLine[1].point[55].y") + " EUR"
Perioda 57
PARSEJSON(output, "$.data.dataLine[1].point[56].y") + " EUR"
Perioda 58
PARSEJSON(output, "$.data.dataLine[1].point[57].y") + " EUR"
Perioda 59
PARSEJSON(output, "$.data.dataLine[1].point[58].y") + " EUR"
Perioda 60
PARSEJSON(output, "$.data.dataLine[1].point[59].y") + " EUR"
Perioda 61
PARSEJSON(output, "$.data.dataLine[1].point[60].y") + " EUR"
Perioda 62
PARSEJSON(output, "$.data.dataLine[1].point[61].y") + " EUR"
Perioda 63
PARSEJSON(output, "$.data.dataLine[1].point[62].y") + " EUR"
Perioda 64
PARSEJSON(output, "$.data.dataLine[1].point[63].y") + " EUR"
Perioda 65
PARSEJSON(output, "$.data.dataLine[1].point[64].y") + " EUR"
Perioda 66
PARSEJSON(output, "$.data.dataLine[1].point[65].y") + " EUR"
Perioda 67
PARSEJSON(output, "$.data.dataLine[1].point[66].y") + " EUR"
Perioda 68
PARSEJSON(output, "$.data.dataLine[1].point[67].y") + " EUR"
Perioda 69
PARSEJSON(output, "$.data.dataLine[1].point[68].y") + " EUR"
Perioda 70
PARSEJSON(output, "$.data.dataLine[1].point[69].y") + " EUR"
Perioda 71
PARSEJSON(output, "$.data.dataLine[1].point[70].y") + " EUR"
Perioda 72
PARSEJSON(output, "$.data.dataLine[1].point[71].y") + " EUR"
Perioda 73
PARSEJSON(output, "$.data.dataLine[1].point[72].y") + " EUR"
Perioda 74
PARSEJSON(output, "$.data.dataLine[1].point[73].y") + " EUR"
Perioda 75
PARSEJSON(output, "$.data.dataLine[1].point[74].y") + " EUR"
Perioda 76
PARSEJSON(output, "$.data.dataLine[1].point[75].y") + " EUR"
Perioda 77
PARSEJSON(output, "$.data.dataLine[1].point[76].y") + " EUR"
Perioda 78
PARSEJSON(output, "$.data.dataLine[1].point[77].y") + " EUR"
Perioda 79
PARSEJSON(output, "$.data.dataLine[1].point[78].y") + " EUR"
Perioda 80
PARSEJSON(output, "$.data.dataLine[1].point[79].y") + " EUR"
Perioda 81
PARSEJSON(output, "$.data.dataLine[1].point[80].y") + " EUR"
Perioda 82
PARSEJSON(output, "$.data.dataLine[1].point[81].y") + " EUR"
Perioda 83
PARSEJSON(output, "$.data.dataLine[1].point[82].y") + " EUR"
Perioda 84
PARSEJSON(output, "$.data.dataLine[1].point[83].y") + " EUR"
Perioda 85
PARSEJSON(output, "$.data.dataLine[1].point[84].y") + " EUR"
Perioda 86
PARSEJSON(output, "$.data.dataLine[1].point[85].y") + " EUR"
Perioda 87
PARSEJSON(output, "$.data.dataLine[1].point[86].y") + " EUR"
Perioda 88
PARSEJSON(output, "$.data.dataLine[1].point[87].y") + " EUR"
Perioda 89
PARSEJSON(output, "$.data.dataLine[1].point[88].y") + " EUR"
Perioda 90
PARSEJSON(output, "$.data.dataLine[1].point[89].y") + " EUR"
Perioda 91
PARSEJSON(output, "$.data.dataLine[1].point[90].y") + " EUR"
Perioda 92
PARSEJSON(output, "$.data.dataLine[1].point[91].y") + " EUR"
Perioda 93
PARSEJSON(output, "$.data.dataLine[1].point[92].y") + " EUR"
Perioda 94
PARSEJSON(output, "$.data.dataLine[1].point[93].y") + " EUR"
Perioda 95
PARSEJSON(output, "$.data.dataLine[1].point[94].y") + " EUR"
Perioda 96
PARSEJSON(output, "$.data.dataLine[1].point[95].y") + " EUR"
Current Price Variable Read-only

Current hour's spot price converted to CZK using the exchange rate variable

numeric Unit: CZK json_path

Current Price

Read
var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
VAR date := NOW();
var periodHour := LENGTH(prices) / 24;	
var actualperiod := FLOOR(((date.hour * 60) + date.minute) * periodHour / 60);

ROUND(GETAT(prices, actualperiod)*kurz)
Current Period Rank Variable Read-only

Rank of the current hour among all daily periods (1 = cheapest)

numeric json_path

Current Period Rank

Read
var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
var byPrice := ORDERINDEX(prices);
VAR date := NOW();
var periodHour := LENGTH(prices) / 24;	
var actualperiod := FLOOR(((date.hour * 60) + date.minute) * periodHour / 60);

INDEXOF(byPrice, actualperiod) + 1
Current Period Rank in Time Window Variable Read-only

Rank of the current hour within the From-To time window (NaN if outside)

numeric json_path

Current Period Rank in Time Window

Read
VAR date := NOW();
IF date.hour < From or date.hour > To or From >= To
	RETURN(NaN);
ELSE
	var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
	var periodHour := LENGTH(prices) / 24;
	var windowPrices := COPY(prices, From * periodHour, (To - From) * periodHour);
	var byPrice := ORDERINDEX(windowPrices);
	
	var actualperiod := FLOOR(((date.hour * 60) + date.minute) * periodHour / 60);

	RETURN(INDEXOF(byPrice, actualperiod - From * periodHour) +1);
END
Currency Exchange Rate Variable

EUR to CZK conversion rate — read/write, used by all price devices

numeric Unit: CZK/EUR

Currency Exchange Rate

Read
kurz
Write
kurz := Va
Highest Price of Day Variable Read-only

Maximum spot price across all periods of the day (CZK)

numeric Unit: CZK json_path

Highest Price of Day

Read
var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
GETAT(ORDERDESC(prices), 0)*kurz
Highest Price in Time Window Variable Read-only

Maximum spot price within the From-To time window (CZK)

numeric Unit: CZK json_path

Highest Price in Time Window

Read
VAR date := NOW();
IF From >= To
	RETURN(NaN);
ELSE
	var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
	var periodHour := LENGTH(prices) / 24;	
	var actualperiod := FLOOR(((date.hour * 60) + date.minute) * periodHour / 60);
	var windowPrices := COPY(prices, From * periodHour, (To - From) * periodHour);
	var byPrice := ORDERDESC(windowPrices);

	RETURN(GETAT(byPrice, 0)*kurz);
END
Lowest Price of Day Variable Read-only

Minimum spot price across all periods of the day (CZK)

numeric Unit: CZK json_path

Lowest Price of Day

Read
var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
GETAT(ORDER(prices), 0)*kurz
Lowest Price in Time Window Variable Read-only

Minimum spot price within the From-To time window (CZK)

numeric Unit: CZK json_path

Lowest Price in Time Window

Read
VAR date := NOW();
IF From >= To
	RETURN(NaN);
ELSE
	var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
	var periodHour := LENGTH(prices) / 24;	
	#var actualperiod := FLOOR(((date.hour * 60) + date.minute) * periodHour / 60);
	var windowPrices := COPY(prices, From * periodHour, (To - From) * periodHour);
	var byPrice := ORDER(windowPrices);

	RETURN(GETAT(byPrice, 0)*kurz);
END
Cheapest Period of Day Variable Read-only

Period number (1-based) of the cheapest hour in the day

numeric json_path

Cheapest Period of Day

Read
var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
GETAT(ORDERINDEX(prices), 0) +1
Cheapest Remaining Period Variable Read-only

Period number of the cheapest hour from the current time onward

numeric json_path

Cheapest Remaining Period

Read
var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
VAR date := NOW();
var periodHour := LENGTH(prices) / 24;	
var actualperiod := FLOOR(((date.hour * 60) + date.minute) * periodHour / 60);
var remainingPrices := COPY(prices, actualperiod, LENGTH(prices) - actualperiod);
var byPrice := ORDERINDEX(remainingPrices);

GETAT(byPrice, 0) + actualperiod +1
Period Count Variable Read-only

Total number of available price periods (typically 96)

numeric json_path

Period Count

Read
var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
LENGTH(prices);
Period Count in Time Window Variable Read-only

Number of periods within the From-To time window

numeric json_path

Period Count in Time Window

Read
VAR date := NOW();
IF From >= To
	RETURN(NaN);
ELSE
	var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
	var periodHour := LENGTH(prices) / 24;
	var windowPrices := COPY(prices, From * periodHour, (To - From) * periodHour);

	RETURN(LENGTH(windowPrices));
END
Average Price of Day Variable Read-only

Average spot price across all periods of the day (CZK)

numeric Unit: CZK json_path

Average Price of Day

Read
var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
AVG(prices)*kurz
Average Price in Time Window Variable Read-only

Average spot price within the From-To time window (CZK)

numeric Unit: CZK json_path

Average Price in Time Window

Read
VAR date := NOW();
IF From >= To
	RETURN(NaN);
ELSE
	var prices := PARSEJSON(output, "$.data.dataLine[1].point..y");
	var periodHour := LENGTH(prices) / 24;	
	#var actualperiod := FLOOR(((date.hour * 60) + date.minute) * periodHour / 60);
	var windowPrices := COPY(prices, From * periodHour, (To - From) * periodHour);

	RETURN(AVG(windowPrices)*kurz);
END
Connection: Packet Parser → HTTP