This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

REST API to KNX groups
#1
Hello,

I'm working on a script to control my heatpump based on actual prices on the electrical market!
So far I have managed to fetch data to the LM but now I don't know how to continue Big Grin

The result contains 24 values, each for every hour of the day.
The response is in EUR/MWh so to convert it to SEK öre/kWh take the price in EUR/MWh (e.g. 11.75) times the currency exchange rate for SEK/EUR (e.g. 10.36) and then we need to divide it with 1000 to get kWh from MWh and again take it times 100 to get öre insted of SEK. We can shorten the MWh->kWh and öre->SEK by instead divide the result by ten (as divided by 1000, times 100 equals out to divided by 10), so e.g. 11.75 EUR/MWh * 10.36 / 10 = 12.173 öre/kWh.

My groupadress 5/5/6 is intended to keep the currency exchange.

I would want every read hour to be sent to a separate KNX group adress.
position 1 = 1/1/1
position 2 = 1/1/2
position 3 = 1/1/3
....
...

Code:
currency  = grp.getvalue('5/5/6')

http = require('socket.http')
ltn12 = require('ltn12')

res, code = http.request({
  url = 'https://web-api.tp.entsoe.eu/api?securityToken=XXXXXXXXXX&documentType=A44&in_Domain=10Y1001A1001A46L&out_Domain=10Y1001A1001A46L&periodStart=202209270000&periodEnd=202209272300',
  method = 'GET',
})

log(res, code)

The output in the log is:


Code:
* arg: 1
  * string: <?xml version="1.0" encoding="UTF-8"?>
<Publication_MarketDocument xmlns="urn:iec62325.351:tc57wg16:451-3:publicationdocument:7:0">
<mRID>5fad2c86f6644f35bfdde1c325b766bf</mRID>
<revisionNumber>1</revisionNumber>
<type>A44</type>
<sender_MarketParticipant.mRID codingScheme="A01">10X1001A1001A450</sender_MarketParticipant.mRID>
<sender_MarketParticipant.marketRole.type>A32</sender_MarketParticipant.marketRole.type>
<receiver_MarketParticipant.mRID codingScheme="A01">10X1001A1001A450</receiver_MarketParticipant.mRID>
<receiver_MarketParticipant.marketRole.type>A33</receiver_MarketParticipant.marketRole.type>
<createdDateTime>2022-09-27T14:43:39Z</createdDateTime>
<period.timeInterval>
<start>2022-09-26T22:00Z</start>
<end>2022-09-28T22:00Z</end>
</period.timeInterval>
<TimeSeries>
<mRID>1</mRID>
<businessType>A62</businessType>
<in_Domain.mRID codingScheme="A01">10Y1001A1001A46L</in_Domain.mRID>
<out_Domain.mRID codingScheme="A01">10Y1001A1001A46L</out_Domain.mRID>
<currency_Unit.name>EUR</currency_Unit.name>
<price_Measure_Unit.name>MWH</price_Measure_Unit.name>
<curveType>A01</curveType>
<Period>
<timeInterval>
<start>2022-09-26T22:00Z</start>
<end>2022-09-27T22:00Z</end>
</timeInterval>
<resolution>PT60M</resolution>
<Point>
<position>1</position>
<price.amount>13.93</price.amount>
</Point>
<Point>
<position>2</position>
<price.amount>13.00</price.amount>
</Point>
<Point>
<position>3</position>
<price.amount>12.66</price.amount>
</Point>
<Point>
<position>4</position>
<price.amount>12.97</price.amount>
</Point>
<Point>
<position>5</position>
<price.amount>14.32</price.amount>
</Point>
<Point>
<position>6</position>
<price.amount>16.20</price.amount>
</Point>
<Point>
<position>7</position>
<price.amount>24.72</price.amount>
</Point>
<Point>
<position>8</position>
<price.amount>59.41</price.amount>
</Point>
<Point>
<position>9</position>
<price.amount>90.16</price.amount>
</Point>
<Point>
<position>10</position>
<price.amount>69.95</price.amount>
</Point>
<Point>
<position>11</position>
<price.amount>69.29</price.amount>
</Point>
<Point>
<position>12</position>
<price.amount>68.76</price.amount>
</Point>
<Point>
<position>13</position>
<price.amount>62.06</price.amount>
</Point>
<Point>
<position>14</position>
<price.amount>60.86</price.amount>
</Point>
<Point>
<position>15</position>
<price.amount>58.48</price.amount>
</Point>
<Point>
<position>16</position>
<price.amount>57.42</price.amount>
</Point>
<Point>
<position>17</position>
<price.amount>58.05</price.amount>
</Point>
<Point>
<position>18</position>
<price.amount>62.05</price.amount>
</Point>
<Point>
<position>19</position>
<price.amount>66.33</price.amount>
</Point>
<Point>
<position>20</position>
<price.amount>65.16</price.amount>
</Point>
<Point>
<position>21</position>
<price.amount>60.39</price.amount>
</Point>
<Point>
<position>22</position>
<price.amount>52.37</price.amount>
</Point>
<Point>
<position>23</position>
<price.amount>46.10</price.amount>
</Point>
<Point>
<position>24</position>
<price.amount>34.73</price.amount>
</Point>
</Period>
</TimeSeries>
<TimeSeries>
<mRID>2</mRID>
<businessType>A62</businessType>
<in_Domain.mRID codingScheme="A01">10Y1001A1001A46L</in_Domain.mRID>
<out_Domain.mRID codingScheme="A01">10Y1001A1001A46L</out_Domain.mRID>
<currency_Unit.name>EUR</currency_Unit.name>
<price_Measure_Unit.name>MWH</price_Measure_Unit.name>
<curveType>A01</curveType>
<Period>
<timeInterval>
<start>2022-09-27T22:00Z</start>
<end>2022-09-28T22:00Z</end>
</timeInterval>
<resolution>PT60M</resolution>
<Point>
<position>1</position>
<price.amount>45.57</price.amount>
</Point>
<Point>
<position>2</position>
<price.amount>45.17</price.amount>
</Point>
<Point>
<position>3</position>
<price.amount>45.81</price.amount>
</Point>
<Point>
<position>4</position>
<price.amount>46.70</price.amount>
</Point>
<Point>
<position>5</position>
<price.amount>47.04</price.amount>
</Point>
<Point>
<position>6</position>
<price.amount>50.63</price.amount>
</Point>
<Point>
<position>7</position>
<price.amount>56.48</price.amount>
</Point>
<Point>
<position>8</position>
<price.amount>69.02</price.amount>
</Point>
<Point>
<position>9</position>
<price.amount>76.26</price.amount>
</Point>
<Point>
<position>10</position>
<price.amount>77.24</price.amount>
</Point>
<Point>
<position>11</position>
<price.amount>76.49</price.amount>
</Point>
<Point>
<position>12</position>
<price.amount>76.34</price.amount>
</Point>
<Point>
<position>13</position>
<price.amount>76.09</price.amount>
</Point>
<Point>
<position>14</position>
<price.amount>76.43</price.amount>
</Point>
<Point>
<position>15</position>
<price.amount>76.81</price.amount>
</Point>
<Point>
<position>16</position>
<price.amount>76.88</price.amount>
</Point>
<Point>
<position>17</position>
<price.amount>72.22</price.amount>
</Point>
<Point>
<position>18</position>
<price.amount>75.92</price.amount>
</Point>
<Point>
<position>19</position>
<price.amount>75.72</price.amount>
</Point>
<Point>
<position>20</position>
<price.amount>75.61</price.amount>
</Point>
<Point>
<position>21</position>
<price.amount>76.76</price.amount>
</Point>
<Point>
<position>22</position>
<price.amount>70.25</price.amount>
</Point>
<Point>
<position>23</position>
<price.amount>70.00</price.amount>
</Point>
<Point>
<position>24</position>
<price.amount>60.17</price.amount>
</Point>
</Period>
</TimeSeries>
</Publication_MarketDocument>
* arg: 2
  * number: 200

One other thing, I would want the following string in the adress to be dynamic and also always be the date of tomorrow, but the time should be static.
"periodStart=202209270000&periodEnd=202209272300"
Reply
#2
Try this:
Code:
date = os.date('%Y%m%d')

periodstart = date .. '0000'
periodend = date .. '2300'

url = 'https://web-api.tp.entsoe.eu/api?securityToken=XXXXXXXXXX&' ..
  'documentType=A44&in_Domain=10Y1001A1001A46L&out_Domain=10Y1001A1001A46L&' ..
  'periodStart=' .. periodstart .. '&periodEnd=' .. periodend

data, code = require('socket.http').request(url)
if not data or code ~= 200 then
  log('request failed', data, code)
  return
end

prices = {}
key = 0

callbacks = {
  StartElement = function(p, tag)
    if tag == 'TimeSeries' then
      key = key + 1
      prices[ key ] = {}
    elseif tag == 'position' then
      state = 'position'
    elseif tag == 'price.amount' then
      state = 'price'
    end
  end,
  EndElement = function(p, tag)
    state = nil
  end,
  CharacterData = function(p, value)
    value = value:trim()

    if #value > 0 then
      if state == 'position' then
        position = tonumber(value)
      elseif state == 'price' then
        prices[ key ][ position ] = tonumber(value)
      end
    end
  end
}

parser = require('lxp').new(callbacks)
parser:parse(data)
parser:close()

if key > 0 then
  rate = grp.getvalue('5/5/6')

  for i, price in pairs(prices[ 1 ]) do
    grp.checkupdate('1/1/' .. i, price * rate / 10)
  end
end
Reply
#3
Thanks!

If I would want to manipulate the "date = os.date('%Y%m%d')" to be tomorrows date, would be a simple adjustment?
In example, September is only 30 days, if plussing 1 it shouldn't be 31, instead October 1st.

Also I'm thinking maybe the REST API with all the currency exchanges would be a little bit advanced.
I've found a JSON API with currency and power units already correct.

https://www.vattenfall.se/api/price/spot...-09-28/SN3
Reply
#4
Use this to calculate tomorrow's date:
Code:
date = os.date('%Y%m%d', os.time() + 86400)
Reply
#5
Thanks Admin,

I went with the JSON API from Vattenfall instead, I have made the code to work. However not the prettiest code Big Grin
I would want that, if the Vattenfall homepage is not responding or something goes wrong fetching info, set all the group adresses to "0"
How should I think when writing that code!?


Also, the timestamp format is YYYY-MM--DD while the DTP 11 is DD-MM-YYYY, so the value in my groupadress is "00-00-2000" after writing.

Code:
require('json')
https = require 'ssl.https'
date = os.date('%Y-%m-%d')



url = 'https://www.vattenfall.se/api/price/spot/pricearea/' .. date .. '/' .. date .. '/SN3'
status = ssl.https.request(url)

data = json.decode(status)

timestamp = data[1].TimeStampDay
    grp.checkupdate('5/5/28', timestamp)

price1 = data[1].Value
    grp.checkupdate('5/5/1', price1)

price2 = data[2].Value
    grp.checkupdate('5/5/2', price2)

price3 = data[3].Value
    grp.checkupdate('5/5/3', price3)

price4 = data[4].Value
    grp.checkupdate('5/5/4', price4)

price5 = data[5].Value
    grp.checkupdate('5/5/5', price5)

price6 = data[6].Value
    grp.checkupdate('5/5/6', price6)

price7 = data[7].Value
    grp.checkupdate('5/5/7', price7)

price8 = data[8].Value
    grp.checkupdate('5/5/8', price8)

price9 = data[9].Value
    grp.checkupdate('5/5/9', price9)

price10 = data[10].Value
    grp.checkupdate('5/5/10', price10)

price11 = data[11].Value
    grp.checkupdate('5/5/11', price11)

price12 = data[12].Value
    grp.checkupdate('5/5/12', price12)

price13 = data[13].Value
    grp.checkupdate('5/5/13', price13)

price14 = data[14].Value
    grp.checkupdate('5/5/14', price14)

price15 = data[15].Value
    grp.checkupdate('5/5/15', price15)

price16 = data[16].Value
    grp.checkupdate('5/5/16', price16)

price17 = data[17].Value
    grp.checkupdate('5/5/17', price17)

price18 = data[18].Value
    grp.checkupdate('5/5/18', price18)

price19 = data[19].Value
    grp.checkupdate('5/5/19', price19)

price20 = data[20].Value
    grp.checkupdate('5/5/20', price20)

price21 = data[21].Value
    grp.checkupdate('5/5/21', price21)

price22 = data[22].Value
    grp.checkupdate('5/5/22', price22)

price23 = data[23].Value
    grp.checkupdate('5/5/23', price23)

price24 = data[24].Value
    grp.checkupdate('5/5/24', price24)





medelpris = price1+price2+price3+price4+price5+price6+price7+price8+price9+price10+price11+price12+price13+price14+price15+price16+price17+price18+price19+price20+price21+price22+price23+price24
medelpris = medelpris / 24
    grp.checkupdate('5/5/25', medelpris)

maxpris = math.max(price1, price2, price3, price4, price5, price6, price7, price8, price9, price10, price11, price12, price13, price14, price15, price16, price17, price18, price19, price20, price21, price22, price23, price24)
    grp.checkupdate('5/5/26', maxpris)

minpris = math.min(price1, price2, price3, price4, price5, price6, price7, price8, price9, price10, price11, price12, price13, price14, price15, price16, price17, price18, price19, price20, price21, price22, price23, price24)
    grp.checkupdate('5/5/27', minpris)


log(price1,price2)
Reply
#6
A for loop makes the code more compact and can't cause copy-paste errors. DPT 11 expects a table with year/month/day, not a string.
Code:
require('json')
https = require 'ssl.https'
date = os.date('%Y-%m-%d')

url = 'https://www.vattenfall.se/api/price/spot/pricearea/' .. date .. '/' .. date .. '/SN3'
status = ssl.https.request(url)

data = json.decode(status)
if type(data) ~= 'table' then
  return
end

ts = data[ 1 ].TimeStampDay
if type(ts) == 'string' then
  ts = ts:split('-')

  grp.checkupdate('5/5/28', {
    year = tonumber(ts[1]),
    month = tonumber(ts[2]),
    day = tonumber(ts[3])
  })
end

min = math.huge
max = -math.huge
cnt = 0
sum = 0

for i, item in ipairs(data) do
  price = item.Value

  min = math.min(min, price)
  max = math.max(max, price)
  sum = sum + price
  cnt = cnt + 1

  grp.checkupdate('5/5/' .. i, price)
end

grp.checkupdate('5/5/25', sum / cnt)
grp.checkupdate('5/5/26', max)
grp.checkupdate('5/5/27', min)
Reply


Forum Jump: