Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
25cfd8f
Attempt 1 at adding Agile Outgoing
tjsaunders Aug 3, 2020
147db84
Attempt 2/fixes
tjsaunders Aug 3, 2020
9ba3603
nursery school fixes
tjsaunders Aug 3, 2020
b9318df
Update octopus.js
tjsaunders Aug 3, 2020
0d13a10
rename node
tjsaunders Aug 3, 2020
70e17a2
fix octopus.js
tjsaunders Aug 3, 2020
d27be64
Trying to unbreak Agile
tjsaunders Aug 3, 2020
2041923
Update octopus.js
tjsaunders Aug 3, 2020
0fe28df
Try switching to n.tariff
tjsaunders Aug 5, 2020
3fefac2
Merge branch 'master' into pr/10
borpin Aug 5, 2020
39dbd80
min/max price block update
tjsaunders Aug 5, 2020
3066619
Merge branch 'dev' of https://github.com/tjsaunders/node-red-contrib-…
tjsaunders Aug 5, 2020
a4c65fe
Update documentation for clarity
tjsaunders Aug 5, 2020
c99aa12
Removed min_block_start from output 1
tjsaunders Aug 5, 2020
9b02555
Consumption with API v0.1
tjsaunders Aug 19, 2020
fc3fcc1
Close all the brackets
tjsaunders Aug 19, 2020
caa3975
Fix spelling mistakes
tjsaunders Aug 19, 2020
3f575bf
More spelling mistakes
tjsaunders Aug 19, 2020
2ec63a6
Authentication on Consumption request
tjsaunders Aug 19, 2020
3c2f49d
Tightened error handling
tjsaunders Aug 19, 2020
04152d7
Removed overriding scope, added protocol to url
tjsaunders Aug 19, 2020
122241f
Trying to get errors to show
tjsaunders Aug 19, 2020
8688dab
Same again
tjsaunders Aug 19, 2020
f9ebb7f
Update octopus.js
tjsaunders Aug 20, 2020
212fb70
Troubleshooting
tjsaunders Aug 20, 2020
a2184fd
Update octopus.js
tjsaunders Aug 20, 2020
c75c574
Debug logging
tjsaunders Aug 20, 2020
5ea6365
Update octopus.js
tjsaunders Aug 20, 2020
12e870b
Update octopus.js
tjsaunders Aug 20, 2020
4a57880
Update octopus.js
tjsaunders Aug 20, 2020
c1d4909
Update octopus.js
tjsaunders Aug 20, 2020
f3e00f5
Update octopus.js
tjsaunders Aug 20, 2020
14f758f
Move node.send
tjsaunders Aug 20, 2020
40ec82b
Define msg4
tjsaunders Aug 20, 2020
26b5199
Removed Debug logging
tjsaunders Aug 26, 2020
b19f394
Changed .html to hide apikey
tjsaunders Sep 16, 2020
7e21f53
fix JSON Key spaces
tjsaunders Oct 13, 2020
736fa2b
Merge remote-tracking branch 'upstream/dev' into dev
tjsaunders Jun 18, 2021
825a6be
Added Octopus Go (test)
tjsaunders Aug 16, 2021
0d4b580
Update octopus.js
tjsaunders Aug 16, 2021
dd24aff
Update octopus.js
tjsaunders Aug 16, 2021
17098bb
Remove random "n" from octopus.js
tjsaunders Dec 24, 2021
6e29bce
sysout apiurl
tjsaunders Dec 24, 2021
595326b
again
tjsaunders Dec 24, 2021
d175f62
Added base url form field
tjsaunders Aug 18, 2022
a181c83
Fix .html omission
tjsaunders Aug 18, 2022
02a5021
Add identifier functionality
tjsaunders Oct 28, 2022
9647751
Update octopus.js
tjsaunders Oct 28, 2022
67b9630
Update octopus.html
tjsaunders Oct 28, 2022
1862c89
Update Influxdb output
tjsaunders Oct 29, 2022
031ca36
Fix influxdb field syntax
tjsaunders Oct 29, 2022
e655b52
Added datatype tag to influxdb output
tjsaunders Oct 29, 2022
456cb41
Add tag for meter type. Change measurement name
tjsaunders Oct 29, 2022
f0aff83
Consumption Meter Type
tjsaunders Dec 3, 2022
c6e9583
type -> metertype
tjsaunders Dec 3, 2022
9dd2229
oop
tjsaunders Dec 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ node-red-contrib-octopus

# Under Development

A <a href="https://nodered.org" target="_new">Node-RED</a> node to extract the <a href="https://octopus.energy" target="_new">Octopus Agile</a> future price data via the API.
A <a href="https://nodered.org" target="_new">Node-RED</a> node to extract the <a href="https://octopus.energy" target="_new">Octopus Agile</a> (or Agile Outgoing) future price data via the API.

My [referral code](https://share.octopus.energy/wise-jade-356) if you are switching to Octopus and feel kind.

Expand Down
78 changes: 62 additions & 16 deletions octopus.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="octopus">
</div>
<div class="form-row">
<label for="node-input-comboidentifier"><i class="fa fa-tasks"></i> Combination Identifier</label>
<input type="text" id="node-input-comboidentifier" placeholder="Site ID here">
</div>
<div class="form-row">
<label for="node-input-region"><i class="fa fa-tag"></i> Energy Region</label>
Expand All @@ -21,16 +25,50 @@
<option value="N">_N - South Scotland</option>
<option value="P">_P - North Scotland</option>
</select>
</div>
<div class="form-row">
<label for="node-input-tariff"><i class="fa fa-tag"></i> Tariff Type</label>
<select id="node-input-tariff" style="width:130px;">
<option value="AGILE">Agile</option>
<option value="OUTGOING">Agile Outgoing</option>
<option value="GO">Go</option>
</select>
</div>
<div class="form-row">
<label for="node-input-baseurl"><i class="fa fa-tasks"></i> Tariff Base URL</label>
<input type="text" id="node-input-baseurl">

</div>
<div class="form-tips">Example: https://api.octopus.energy/v1/products/GO-22-03-29/electricity-tariffs/E-1R-GO-22-03-29-</div>
<div class="form-row">
<label for="node-input-minmax"><i class="fa fa-tag"></i> Minimum vs Maximum</label>
<select id="node-input-minmax" style="width:300px;">
<option value="MIN">Minimum Price (cheapest)</option>
<option value="MAX">Maximum Price (most expensive)</option>
</select>
<div class="form-tips">Whether to calculate the minimum or maximum priced block using the <b>Period length</b> below.</div>
</div>
<div class="form-row">
<label for="node-input-numblocks"><i class="fa fa-globe"></i> Period length</label>
<input type="text" id="node-input-numblocks">
</div>
<!-- <div class="form-row">
<label for="node-config-input-apikey"><i class="fa fa-tasks"></i> API key</label>
<input type="text" id="node-config-input-apikey">
</div> -->
</div></div>
<div class="form-tips">The <b>Period length</b> is the number of 30 minute blocks to average to find cheapest period of that length (e.g. 2,4 for 60 min and 120min periods).</div>
<div class="form-row">
<label for="node-input-apikey"><i class="fa fa-tasks"></i> API key</label>
<input type="password" id="node-input-apikey">
</div>
<div class="form-row">
<label for="node-input-consumptionurl"><i class="fa fa-tasks"></i> API Consumption URL</label>
<input type="text" id="node-input-consumptionurl">
</div>
<div class="form-tips">Example: /v1/electricity-meter-points/MPAN/meters/SERIAL/consumption/?page_size=5000</div>
<div class="form-row">
<label for="node-input-metertype"><i class="fa fa-tag"></i> Meter Type</label>
<select id="node-input-metertype" style="width:130px;">
<option value="GAS">Gas</option>
<option value="ELECTRICITY">Electricity</option>
</select>
</div>
</script>

<script type="text/x-red" data-help-name="octopus in">
Expand Down Expand Up @@ -66,22 +104,22 @@ <h3>Outputs</h3>
<dd>Minimum Price within available data</dd>
<dt>payload.max_price_inc_vat<span class="property-type">float</span></dt>
<dd>Maximum Price within available data</dd>
<dt>min_blocks<span class="property-type">Array</span></dt>
<dd>An array of minimum price blocks</dd>
<dt>minmax_blocks<span class="property-type">Array</span></dt>
<dd>An array of price blocks, either minimum or maximum depending on option chosen</dd>
<ul>
<dt>min Block Price<span class="property-type">float</span></dt>
<dt>min/max Block Price<span class="property-type">float</span></dt>
<dd>Block Price (average over periods)</dd>
<dt>min Block valid From<span class="property-type">ISO Date</span></dt>
<dt>min/max Block valid From<span class="property-type">ISO Date</span></dt>
<dd>Valid from (when block period starts)</dd>
<dt>min_block_size_mins<span class="property-type">integer</span></dt>
<dt>block_size_mins<span class="property-type">integer</span></dt>
<dd>Length of block in minutes</dd>
</ul>
</dl>
</li>
<li>InfluxDB Formatted Data (influxdb out node)
<dl class="message-properties">
<dt>payload<span class="property-type">array</span></dt>
<dd>Array of data pairs of value and time. The 'source' is set to 'Agile'</dd>
<dd>Array of data pairs of value and time. The 'source' is set to 'Agile' or 'Outgoing' depending on the tariff chosen</dd>
<dt>measurement<span class="property-type">string</span></dt>
<dd>A value for the InfluxDB measurement value (default OctopusPrice).
Can be overwritten in the InfluxDB input node</dd>
Expand All @@ -90,7 +128,7 @@ <h3>Outputs</h3>
</ol>

<h3>Details</h3>
<p>Query the Octopus Agile API for future pricing.
<p>Query the Octopus Agile, Octopus Go (or Agile Outgoing) API for future pricing.
<p>The pricing data is broken up into 30 min periods. The data for the next 24Hrs (23:00Z - 23:00Z) is released
at approximately 16:00Z. The request is set to obtain all data available from now so the number of data periods
returned by the API varies between approximately 15 and 63.
Expand All @@ -111,15 +149,23 @@ <h3>References</h3>
defaults: {
name: {value:"Octopus"},
region: {value:"A"},
numblocks: {value:"4"}
baseurl: {value:""},
tariff: {value:"AGILE"},
minmax: {value:"MIN"},
numblocks: {value:"4"},
apikey: {value:"none"},
consumptionurl: {value:""},
comboidentifier: {value:"",required: true},
metertype: {value:"",required: true}

},
inputs:1,
outputs:3,
outputLabels: ["raw","processed","influxdb"],
outputs:4,
outputLabels: ["raw","processed","influxdb","influxdbconsumption"],
icon: "octopus.png",
align: "right",
label: function() {
return this.name||this.region;
return this.name||this.region||this.baseurl||this.tariff||this.minmax||this.apikey||this.consumptionurl||this.comboidentifier||this.metertype;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
Expand Down
136 changes: 112 additions & 24 deletions octopus.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,34 @@ module.exports = function(RED) {
var node = this;

var num_blocks = [];
if (n.numblocks !== undefined) {
num_blocks = n.numblocks.split(",").map(function(item) {
if (n.numblocks !== undefined) {
return parseInt(item.trim());
});
}

this.region = n.region
};
});


var baseurl = n.baseurl;
var influxDBsource ="";
var consumptionDBsource = {"source" : "Agile"};


var baseurl = "https://api.octopus.energy/v1/products/AGILE-18-02-21/electricity-tariffs/E-1R-AGILE-18-02-21-";
if (n.tariff == "OUTGOING") {
if (n.baseurl == "") {
baseurl = "https://api.octopus.energy/v1/products/AGILE-OUTGOING-19-05-13/electricity-tariffs/E-1R-AGILE-OUTGOING-19-05-13-";
}
influxDBsource = "Outgoing";
} else if (n.tariff == "AGILE") {
if (n.baseurl == "") {
baseurl = "https://api.octopus.energy/v1/products/AGILE-18-02-21/electricity-tariffs/E-1R-AGILE-18-02-21-";
}
influxDBsource = "Agile";
} else {
if (n.baseurl == "") {
baseurl = "https://api.octopus.energy/v1/products/GO-22-07-05/electricity-tariffs/E-1R-GO-22-07-05-";
}
influxDBsource = "Go";
}
var https = require("https");
var next_run = new Date(0);

Expand All @@ -51,10 +70,11 @@ module.exports = function(RED) {
// add start and end used to msg - strip milliseconds
msg.start_time = start_time.replace(/\.[0-9]{3}/, '');
msg.end_time = end_time.replace(/\.[0-9]{3}/, '');
msg.region = this.region;

var APIurl = baseurl + this.region + '/standard-unit-rates/?' + 'period_from=' + start_time + '&' + 'period_to=' + end_time;
msg.region = n.region;

var APIurl = baseurl + n.region + '/standard-unit-rates/?' + 'period_from=' + start_time + '&' + 'period_to=' + end_time;

console.log(APIurl);
https.get(APIurl, function(res) {
msg.rc = res.statusCode;
msg.payload = "";
Expand All @@ -78,7 +98,15 @@ module.exports = function(RED) {
// Extract min and max prices from available data
msg2.payload.min_price_inc_vat = Math.min(...msg.price_array);
msg2.payload.max_price_inc_vat = Math.max(...msg.price_array);


var metertype = "";
if (n.metertype == "GAS") {
metertype = "gas";
}
else {
metertype = "electricity";
}

let blocks_output = [];
// put prices array now -> future
var price_array_rev = msg.price_array.reverse();
Expand All @@ -94,33 +122,93 @@ module.exports = function(RED) {
}
// blocks are now listed in same order as main data (push each item of an array reverses it)
// msg.blocks = blocks_result;
let min_block_start = blocks_result.indexOf(Math.min(...blocks_result)) + block - 1;
msg.min_block_start = min_block_start;

blocks_output.push({ "min Block Price": Math.min(...blocks_result), "min Block valid From":msg.payload.results[min_block_start].valid_from, "min_block_size_mins": block * 30 });
let minmax_block_start = "";
if (n.minmax == "MIN") {
minmax_block_start = blocks_result.indexOf(Math.min(...blocks_result)) + block - 1;
blocks_output.push({ "min_block_price": Math.min(...blocks_result), "min_block_valid_from":msg.payload.results[minmax_block_start].valid_from, "block_size_mins": block * 30, identifier: n.comboidentifier });
} else if (n.minmax == "MAX") {
minmax_block_start = blocks_result.indexOf(Math.max(...blocks_result)) + block - 1;
blocks_output.push({ "max_block_price": Math.max(...blocks_result), "max_block_valid_from":msg.payload.results[minmax_block_start].valid_from, "block_size_mins": block * 30, identifier: n.comboidentifier });
}

//blocks_output.push({ "min Block Price": Math.min(...blocks_result), "min Block valid From":msg.payload.results[minmax_block_start].valid_from, "min_block_size_mins": block * 30 });
// msg2.payload.min_block = { "min Block Price": Math.min(...blocks_result), "min Block valid From":msg.payload.results[min_block_start].valid_from, "min_block_size_mins": num_blocks * 30 };
}
});
msg2.payload.min_blocks = blocks_output;
msg2.payload.minmax_blocks = blocks_output;

var msg3 = {};
msg3.payload = [];

msg.payload.results.forEach(function(item, index) {
msg3.payload.push([{ value_inc_vat : item.value_inc_vat,
"time": new Date(item.valid_from).getTime() *1000 *1000}, {"source" : "Agile"}]);
msg.payload.results.forEach(function(item, index) {
msg3.payload.push([{ value_inc_vat : item.value_inc_vat, "time": new Date(item.valid_from).getTime() *1000 *1000},{"identifier": n.comboidentifier, data: "tariff", meter: metertype , source: influxDBsource}]);
});

msg3.measurement = "OctopusPrice";
msg3.measurement = "octopus";

next_run = next_half_hour;
}
catch(err) {



var outputx = {};
var msg4 = {};
if (n.apikey != "none") {

var options = {
host: 'api.octopus.energy',
port: 443,
path: n.consumptionurl,
// authentication headers
headers: {
'Authorization': 'Basic ' + new Buffer(n.apikey).toString('base64')
}
};

https.get(options, function(resc) {
outputx.rc = resc.statusCode;
outputx.payload = [];
resc.setEncoding('utf8');
resc.on('data', function(chunk) {
outputx.payload += chunk;
});
resc.on('end', function() {
if (outputx.rc === 200) {
try {
outputx.payload = JSON.parse(outputx.payload);
msg4.payload = [];

outputx.payload.results.forEach(function(item, index) {
msg4.payload.push([{ consumption : item.consumption, "time": new Date(item.interval_start).getTime() *1000 *1000},{"identifier": n.comboidentifier, data: "consumption", meter: metertype, source: influxDBsource}]);
});
msg4.measurement = "octopus";
node.send([msg, msg2, msg3, msg4]);
} catch(err) {
node.error(err,outputx);
// Failed to parse, pass it on
}
}

});
}).on('error', function(e) {
node.error(e,outputx);
});
} else {
node.send([msg, msg2, msg3, msg4]);
}







} catch(err) {
node.error(err,msg);


// Failed to parse, pass it on
}
// set time for next request on success
node.send([msg, msg2, msg3]);

}
});
}).on('error', function(e) {
Expand Down
Loading