Skip to content

Instantly share code, notes, and snippets.

@andrewreid
Last active March 20, 2025 08:16
Show Gist options
  • Save andrewreid/0bc32e3574ab7c53c3d35cb4419f5fe8 to your computer and use it in GitHub Desktop.
Save andrewreid/0bc32e3574ab7c53c3d35cb4419f5fe8 to your computer and use it in GitHub Desktop.
Node-RED flow for importing Amber Electric wholesale electricity prices into VRM
[
{
"id": "ede00cd79443d77a",
"type": "vrm-api",
"z": "405cbd599f05d654",
"vrm": "9fb7325ee595c4a0",
"name": "Change Dynamic ESS settings",
"api_type": "installations",
"idUser": "",
"users": "",
"idSite": "510670",
"installations": "patch-dynamic-ess-settings",
"attribute": "",
"stats_interval": "",
"show_instance": false,
"stats_start": "",
"stats_end": "",
"use_utc": false,
"gps_start": "",
"gps_end": "",
"widgets": "",
"instance": "",
"vrm_id": "",
"country": "",
"b_max": "",
"tb_max": "",
"fb_max": "",
"tg_max": "",
"fg_max": "",
"b_cycle_cost": "",
"buy_price_formula": "",
"sell_price_formula": "",
"green_mode_on": "",
"feed_in_possible": "",
"feed_in_control_on": "",
"b_goal_hour": "",
"b_goal_SOC": "",
"store_in_global_context": false,
"verbose": false,
"x": 890,
"y": 400,
"wires": [
[
"18ffab3c077f6ef2"
]
]
},
{
"id": "50fd4622ae311d59",
"type": "function",
"z": "405cbd599f05d654",
"name": "Set Dynamic ESS prices",
"func": "// Extract the intervals from the payload\nconst intervals = msg.payload;\n\n// Create empty arrays to hold buy and sell schedules\nlet buySchedule = [];\nlet sellSchedule = [];\n\n// Helper function to convert UTC time to local time\nconst toLocalTime = (isoTime) => {\n const date = new Date(isoTime);\n const options = {\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone // Local timezone\n };\n let time = new Intl.DateTimeFormat('en-US', options).format(date);\n\n // Fix for midnight time formatting\n if (time.startsWith(\"24:\")) {\n time = time.replace(\"24:\", \"00:\");\n }\n\n return time;\n};\n\n// Iterate through each interval and sort data into buy and sell schedules\nintervals.forEach(interval => {\n const from = toLocalTime(interval.startTime); // Convert to local time\n const to = toLocalTime(interval.endTime); // Convert to local time\n\n // Extract and format price\n let price = parseFloat(interval.perKwh);\n\n if (interval.channelType === \"general\") {\n // Convert buy price to dollars and round to the nearest cent\n price = Math.round(price) / 100;\n buySchedule.push({ from: from, to: to, price: price });\n } else if (interval.channelType === \"feedIn\") {\n // Invert sell price, convert to dollars, and round to the nearest cent\n price = Math.round(-price) / 100;\n sellSchedule.push({ from: from, to: to, price: price });\n }\n});\n\n// Create the final output structure\nconst buyPriceSchedule = [{\n days: [0, 1, 2, 3, 4, 5, 6], // All days of the week\n schedule: buySchedule\n}];\n\nconst sellPriceSchedule = [{\n days: [0, 1, 2, 3, 4, 5, 6], // All days of the week\n schedule: sellSchedule\n}];\n\n// Assign the formatted schedules to the payload\nmsg.payload = {\n buyPriceSchedule: JSON.stringify(buyPriceSchedule),\n sellPriceSchedule: JSON.stringify(sellPriceSchedule)\n};\n\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 610,
"y": 340,
"wires": [
[
"d1263bc8dc5a4fa3",
"ede00cd79443d77a"
]
]
},
{
"id": "18ffab3c077f6ef2",
"type": "debug",
"z": "405cbd599f05d654",
"name": "debug 1",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 940,
"y": 460,
"wires": []
},
{
"id": "d1263bc8dc5a4fa3",
"type": "debug",
"z": "405cbd599f05d654",
"name": "DESS prices debug",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 850,
"y": 340,
"wires": []
},
{
"id": "04e22901d3bfa99b",
"type": "http request",
"z": "405cbd599f05d654",
"name": "Get forecast prices",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "https://api.amber.com.au/v1/sites/SITE_ID/prices",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "bearer",
"senderr": false,
"headers": [],
"x": 310,
"y": 340,
"wires": [
[
"50fd4622ae311d59"
]
]
},
{
"id": "c014fe04f830ada0",
"type": "inject",
"z": "405cbd599f05d654",
"name": "",
"props": [
{
"p": "payload"
}
],
"repeat": "300",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "str",
"x": 110,
"y": 340,
"wires": [
[
"04e22901d3bfa99b"
]
]
},
{
"id": "9fb7325ee595c4a0",
"type": "config-vrm-api",
"name": "VRM-Wattle Park"
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment