TapHome

OKTE Spot Prices

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

OKTE (Organizator kratkobokeho trhu s elektrinou) is the Slovak electricity market operator. It publishes day-ahead market (DAM) spot prices — the hourly wholesale price of electricity in EUR/MWh determined by the previous day’s auction.

The TapHome template fetches these prices from the public OKTE API (isot.okte.sk) via HTTP Packet Parser. It provides the current hour’s price, price rankings, min/max/average calculations, and time-window analysis — all updated automatically every hour. No authentication is required.

Configuration

The template connects to isot.okte.sk over HTTPS (port 443). No IP address or credentials are needed — the endpoint is a public API.

During template import, two optional custom variables can be set:

  • From — start hour of a custom time window (0–23, default: 9)
  • To — end hour of a custom time window (0–23, default: 17)

These variables control the “in Time Window” device variants. The defaults (9–17) cover standard working hours. If not needed, leave them at their defaults — the full-day devices work independently.

How it works

The module polls the OKTE DAM API once per hour (3600 s interval). The API request uses today’s date:

1
GET /api/v1/dam/results?deliveryDayFrom={today}&deliveryDayTo={today}

The response contains an array of price objects, each with a period number and a price in EUR/MWh. Depending on the time of day and OKTE publication schedule, the response may include prices for today only (24 periods) or for up to 3 days (72 periods).

Each device script parses this JSON array using PARSEJSON(output, "$..price") and applies sorting, filtering, or aggregation functions to derive its value.

Device capabilities

The template exposes 13 sensor devices. All are read-only — they derive values from the same API response.

Current price and ranking
  • Current Price — the spot price for the current hourly period in EUR/MWh. The script determines the current period index from NOW() and returns the corresponding price from the array.
  • Current Period Rank — the rank of the current hour’s price among all available periods (1 = cheapest). Useful for automation rules like “run the heat pump only when the current hour is among the 8 cheapest.”
  • Current Period Rank in Time Window — same as above, but limited to the user-defined From–To window. Returns NaN if the current hour is outside the window or if From >= To.
Price extremes
  • Highest Price of Day — the maximum spot price across all available periods
  • Lowest Price of Day — the minimum spot price across all available periods
  • Highest Price in Time Window — maximum price within the From–To window
  • Lowest Price in Time Window — minimum price within the From–To window
Averages
  • Average Price of Day — arithmetic mean of all available period prices
  • Average Price in Time Window — arithmetic mean within the From–To window
Period identification
  • Cheapest Period of Day — the 1-based period number of the cheapest hour (e.g., 3 means 02:00–03:00)
  • Cheapest Remaining Period — the cheapest hour from the current hour onward. Updates dynamically as the day progresses.
Counts
  • Period Count — total number of price periods in the API response (typically 24 or 72)
  • Period Count in Time Window — number of periods within the From–To window

Service attributes

The module exposes up to 96 service attributes (Period 1 through Period 96), each showing the spot price for that specific hourly period as a string with “EUR” suffix. In practice, 24 attributes are populated for a single day, or 72 for three days.

These attributes provide a full price schedule visible in the TapHome module detail view.

Custom variables (From / To)

Several devices have “in Time Window” variants that filter the price data to a subset of hours. The window is defined by two custom variables set during template import:

  • From — the starting hour (inclusive), value 0–23
  • To — the ending hour (exclusive), value 0–23

For example, setting From=8 and To=16 analyzes prices only between 08:00 and 16:00. This is useful when you want to optimize energy usage during specific hours — such as running appliances only during the cheapest hours of your working day.

If From >= To, all windowed devices return NaN.

Automation examples

Run devices during cheapest hours

Use the Current Period Rank device in a smart rule condition. For example, to run a pool pump only during the 6 cheapest hours of the day:

  • Condition: Current Period Rank <= 6
  • Action: Turn on pool pump
Avoid peak prices

Use the Current Price device with a threshold:

  • Condition: Current Price > 150 (EUR/MWh)
  • Action: Turn off non-essential loads
Time-window optimization

Set From=22, To=6 to define an overnight window, then use Lowest Price in Time Window or Current Period Rank in Time Window to schedule battery charging or water heater operation during the cheapest overnight hours.

Troubleshooting

All devices show zero or no data
  1. Check internet connectivity on TapHome Core — the template requires outbound HTTPS access to isot.okte.sk
  2. Verify the API is responding — open https://isot.okte.sk/api/v1/dam/results?deliveryDayFrom=2026-03-28&deliveryDayTo=2026-03-28 in a browser
  3. DAM prices for the next day are typically published after 12:45 CET. Before publication, requests for tomorrow’s date return empty results.
Windowed devices return NaN
  1. Verify that From < To in the template import parameters
  2. Check that the current hour falls within the From–To range (for Current Period Rank in Time Window)
  3. Remember that From and To use 24-hour format (0–23)
Price values seem incorrect

OKTE publishes prices in EUR/MWh. To convert to EUR/kWh (household scale), divide by 1000. The template displays raw values from the API without conversion.

Available devices

OKTE Spot Prices Module
Service Attributes
Period 1
Period 2
Period 3
Period 4
Period 5
Period 6
Period 7
Period 8
Period 9
Period 10
Period 11
Period 12
Period 13
Period 14
Period 15
Period 16
Period 17
Period 18
Period 19
Period 20
Period 21
Period 22
Period 23
Period 24
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

okte.sk

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

Spot price for the current hourly period in EUR/MWh — dynamically resolved from the price array based on current time

numeric Unit: EUR json_path

Current Price

Read
var prices := PARSEJSON(output, "$..price");
VAR date := NOW();
var periodHour := LENGTH(prices) / 24;	
var actualperiod := FLOOR(((date.hour * 60) + date.minute) * periodHour / 60);

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

Rank of the current hour's price among all periods (1 = cheapest hour of the day)

numeric json_path

Current Period Rank

Read
var prices := PARSEJSON(output, "$..price");
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's price within a user-defined From–To time window — returns NaN outside the window

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, "$..price");
	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
Highest Price of Day Variable Read-only

Maximum spot price across all available periods (EUR/MWh)

numeric Unit: EUR json_path

Highest Price of Day

Read
var prices := PARSEJSON(output, "$..price");
GETAT(ORDERDESC(prices), 0)
Highest Price in Time Window Variable Read-only

Maximum spot price within the user-defined From–To time window (EUR/MWh)

numeric Unit: EUR json_path

Highest Price in Time Window

Read
VAR date := NOW();
IF From >= To
	RETURN(NaN);
ELSE
	var prices := PARSEJSON(output, "$..price");
	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));
END
Lowest Price of Day Variable Read-only

Minimum spot price across all available periods (EUR/MWh)

numeric Unit: EUR json_path

Lowest Price of Day

Read
var prices := PARSEJSON(output, "$..price");
GETAT(ORDER(prices), 0)
Lowest Price in Time Window Variable Read-only

Minimum spot price within the user-defined From–To time window (EUR/MWh)

numeric Unit: EUR json_path

Lowest Price in Time Window

Read
VAR date := NOW();
IF From >= To
	RETURN(NaN);
ELSE
	var prices := PARSEJSON(output, "$..price");
	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));
END
Cheapest Period of Day Variable Read-only

1-based period number of the cheapest hour in the day (e.g., 3 = 02:00–03:00)

numeric json_path

Cheapest Period of Day

Read
var prices := PARSEJSON(output, "$..price");
GETAT(ORDERINDEX(prices), 0) +1
Cheapest Remaining Period Variable Read-only

1-based period number of the cheapest hour from the current hour onward

numeric json_path

Cheapest Remaining Period

Read
var prices := PARSEJSON(output, "$..price");
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 hourly price periods available in the API response (typically 24 or 72)

numeric json_path

Period Count

Read
var prices := PARSEJSON(output, "$..price");
LENGTH(prices);
Period Count in Time Window Variable Read-only

Number of hourly price periods within the user-defined 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, "$..price");
	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

Arithmetic mean of all available period prices (EUR/MWh)

numeric Unit: EUR json_path

Average Price of Day

Read
var prices := PARSEJSON(output, "$..price");
AVG(prices)
Average Price in Time Window Variable Read-only

Arithmetic mean of spot prices within the user-defined From–To time window (EUR/MWh)

numeric Unit: EUR json_path

Average Price in Time Window

Read
VAR date := NOW();
IF From >= To
	RETURN(NaN);
ELSE
	var prices := PARSEJSON(output, "$..price");
	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));
END
Connection: Packet Parser → HTTP