Well, I figurred it out.
This is what the VRM API Node says:
Check the note:
The API always returns buckets of 15 minute intervals. If you have hourly prices configured for your site, the buckets will contain the same price for 4 records in a row. The records returned will contain data from the beginning of today until the end of tomorrow (if the prices are in, else until the end of today).
This is clearly incorrect, here is the url this call produces:
“https://vrmapi.victronenergy.com/v2/installations/xxxxxx/stats?type=dynamic_ess&interval=hours&start=1761004800&end=1761177599”
interval, start and end values are incorrect interval should have been “15mins” and the start and end time seems to be UTC
Then inserting your query function (slightly adapted to my flow) between the inject node and the VRM-API node produces the correct url:
“https://vrmapi.victronenergy.com/v2/installations/xxxxxx/stats?type=dynamic_ess&interval=15mins&start=1760997600&end=1761170399”
Fun fact, if I call the original installation stats set to 15 minutes:
The url will still use hours instead of 15mins:
“https://vrmapi.victronenergy.com/v2/installations/xxxxxx/stats?type=dynamic_ess&interval=hours&start=1760997600&end=1761170399”
But if I insert the same query function again, it also produces the correct url:
“https://vrmapi.victronenergy.com/v2/installations/xxxxxx/stats?type=dynamic_ess&interval=15mins&start=1760997600&end=1761170399”
So now I truly wonder why VRM-API had to be changed at all when all we actually needed was the correct API documentation on how to construct the query. Or even better, installation stats actually using the interval selected in the node, as could reasonably expected, why else have it selectable in the first place.
And I hear you and generally speaking agree when you say that:
But this has clearly been a botch job on Victron’s end, with all due respect for their dev’s being extremely time constraint and all, not the dev’s fault but management decision on priorities. But between you and myself we must have spend a multitude of valuable professional engineering hours to figure this all out compared to the time it took a Victron employee to break it. And I do not know about you but I do value my time running a single person start-up trying to make ends meet. The idea that this is an acceptable normal way for us (community beta testers) to cooperate with Victron towards a common goal really does not sit well with me. Victron could at least man up and admit they did botch this job up and I would not have any problems accepting compensation for doing what is basically their job , send us a couple of multiplusses of choice for our troubles would go long way towards making this worth our/my contribution to the good cause. Doing this for free to counter the lack of any regression testing at Victron’s end is not a sustainable cooperation model.
Final words: “If it ain’t broken, don’t fix it”
Here the slightly modified query function:
const getStartOfDay = (date) => {
const start = new Date(date)
start.setHours(0, 0, 0, 0)
return Math.floor(start.getTime() / 1000)
}
const now = new Date()
const start = getStartOfDay(now)
const endOfTomorrow = new Date(now)
endOfTomorrow.setDate(endOfTomorrow.getDate() + 1)
endOfTomorrow.setHours(23, 59, 59, 999)
const end = Math.floor(endOfTomorrow.getTime() / 1000)
const siteId = flow.get('siteId')
msg.method = 'get'
msg.url = 'https://vrmapi.victronenergy.com/v2'
msg.query = `installations/${siteId}/stats?type=dynamic_ess&interval=15mins&start=${start}&end=${end}`
msg.topic = 'installations fetch-dynamic-ess-schedules'
return msg;
And flow:
[
{
"id": "cef9dffd6d2982d6",
"type": "group",
"z": "bf1f8a77236fa165",
"style": {
"stroke": "#999999",
"stroke-opacity": "1",
"fill": "none",
"fill-opacity": "1",
"label": true,
"label-position": "nw",
"color": "#a4a4a4"
},
"nodes": [
"b660b2324cc8eb6f",
"013ab2c69f508dcd",
"49d176854b3e2b4c",
"c19d2568d02b223b",
"b01e44bd4918434b",
"4d19309a45dded93"
],
"x": 74,
"y": 79,
"w": 392,
"h": 202
},
{
"id": "b660b2324cc8eb6f",
"type": "debug",
"z": "bf1f8a77236fa165",
"g": "cef9dffd6d2982d6",
"name": "msg object",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "true",
"targetType": "full",
"statusVal": "payload",
"statusType": "auto",
"x": 350,
"y": 240,
"wires": []
},
{
"id": "013ab2c69f508dcd",
"type": "vrm-api",
"z": "bf1f8a77236fa165",
"g": "cef9dffd6d2982d6",
"vrm": "cb2c784a33130f48",
"name": "",
"api_type": "installations",
"idUser": "",
"users": "",
"idSite": "{{flow.siteId}}",
"installations": "fetch-dynamic-ess-schedules",
"attribute": "Bc",
"stats_interval": "",
"show_instance": false,
"stats_start": "",
"stats_end": "",
"use_utc": false,
"gps_start": "",
"gps_end": "",
"widgets": "",
"instance": "",
"store_in_global_context": true,
"verbose": true,
"x": 270,
"y": 200,
"wires": [
[
"b660b2324cc8eb6f"
]
]
},
{
"id": "49d176854b3e2b4c",
"type": "function",
"z": "bf1f8a77236fa165",
"g": "cef9dffd6d2982d6",
"name": "query",
"func": "const getStartOfDay = (date) => {\n const start = new Date(date)\n start.setHours(0, 0, 0, 0)\n return Math.floor(start.getTime() / 1000)\n}\n\nconst now = new Date()\n\nconst start = getStartOfDay(now)\n \nconst endOfTomorrow = new Date(now)\nendOfTomorrow.setDate(endOfTomorrow.getDate() + 1)\nendOfTomorrow.setHours(23, 59, 59, 999)\nconst end = Math.floor(endOfTomorrow.getTime() / 1000)\n\nconst siteId = flow.get('siteId')\n\nmsg.method = 'get'\nmsg.url = 'https://vrmapi.victronenergy.com/v2'\nmsg.query = `installations/${siteId}/stats?type=dynamic_ess&interval=15mins&start=${start}&end=${end}`\nmsg.topic = 'installations fetch-dynamic-ess-schedules'\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 390,
"y": 160,
"wires": [
[
"013ab2c69f508dcd"
]
]
},
{
"id": "c19d2568d02b223b",
"type": "inject",
"z": "bf1f8a77236fa165",
"g": "cef9dffd6d2982d6",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "0.1",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 180,
"y": 160,
"wires": [
[
"49d176854b3e2b4c"
]
]
},
{
"id": "b01e44bd4918434b",
"type": "function",
"z": "bf1f8a77236fa165",
"g": "cef9dffd6d2982d6",
"name": "set flow siteId",
"func": "flow.set('siteId', msg.payload)\nreturn\n// use : {{flow.siteId}} ",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 360,
"y": 120,
"wires": [
[]
]
},
{
"id": "4d19309a45dded93",
"type": "inject",
"z": "bf1f8a77236fa165",
"g": "cef9dffd6d2982d6",
"name": "VRM Site ID",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "siteId",
"payload": "123456",
"payloadType": "num",
"x": 190,
"y": 120,
"wires": [
[
"b01e44bd4918434b"
]
]
},
{
"id": "cb2c784a33130f48",
"type": "config-vrm-api",
"name": "VRM"
}
]

