Public
Edited
Sep 8, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
facet: {
y: "dayOfYearYear",
data: aggregate_consumption_and_tariffs,
label: "Day"
},
// Facet by week, group by day where consumption and rates are summed

marks: [
Plot.ruleY([0]),
// Plot.areaY(aggregate_consumption_and_tariffs, {
// x: "validFrom",
// y1: "flexCost",
// y2: "agileCost",
// curve: "step",
// fill: (d) => (d["flexCost"] < d["agileCost"] ? "flex" : "agile"),
// z: null,
// tip: true
// }),
Plot.barY(aggregate_consumption_and_tariffs, {
x: "hourFrom",
y: "saving",
filter: (d) => d.tariff === "agile",
tip: true,
fill: "grey"
}),
Plot.lineY(aggregate_consumption_and_tariffs, {
x: "hourFrom",
y: "costGbp",
stroke: "tariff",
marker: true
})
// Plot.lineY(aggregate_consumption_and_tariffs, {
// x: "validFrom",
// y: "flexCost",
// curve: "step"
// })
],

color: {
legend: true,
scheme: "RdYlBu"
},

y: {
label: "GBP (VAT Inc)",
grid: true
},

x: {
label: "Time of Day (GMT)",
tickRotate: -55,
tickPadding: 5
},
width,
marginLeft: 50,
marginBottom: 70,
marginRight: 125
})
Insert cell
aggregate_consumption_and_tariffs
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
aggregate_consumption_and_tariffs = {
const flexUnitRate = 31.56;
const aggregated = [];
for (let i = 0; i < half_hourly_meter_consumption.length; i++) {
const consumption = half_hourly_meter_consumption[i];
const agileTariffRates = half_hourly_agile_tariffs[i];
const flexCost = (consumption.consumptionKwh * flexUnitRate) / 100;
const agileCost =
(consumption.consumptionKwh * agileTariffRates.pencePerKwhIncVat) / 100;
const saving = flexCost - agileCost;

const flex = {
...consumption,
tariff: "flex",
costGbp: flexCost,
saving
};

const agile = {
...consumption,
tariff: "agile",
costGbp: agileCost,
saving
};

aggregated.push(flex, agile);
}

return aggregated;
}
Insert cell
half_hourly_meter_consumption = fetch(
`https://api.octopus.energy/v1/electricity-meter-points/${mpan}/meters/${serialNumber}/consumption?period_from=${startDate.toISOString()}&order_by=-period&page_size=2500`,
{
headers: {
Authorization: `Basic ${btoa(octopusApiKey)}:`
}
}
)
.then((response) => response.json())
.then((json) => {
// if (json.next !== null) {
// throw new Error("Response has more values with `next`");
// }

const parsed = json.results.map((r) => {
const validFrom = new Date(r.interval_start);
const validTo = new Date(r.interval_end);
return {
consumptionKwh: r.consumption,
validFrom,
validTo,
dayOfYearYear: dayOfYearYear(validFrom),
weekOfYearYear: weekOfYearYear(validFrom),
day: d3.timeFormat("%a")(validFrom),
hourFrom: timeOfDay(validFrom),
hourTo: timeOfDay(validFrom) === "23:30" ? "24:00" : timeOfDay(validTo)
};
});

return parsed;
})
Insert cell
get_raw_half_hourly_meter_consumption = function (url) {
const rawResults = [];
const urlQueue = [url];

let nextUrl = undefined;
while nextUrl {
fetch(nextUrl, {
headers: {
Authorization: `Basic ${btoa(octopusApiKey)}:`
}
})
.then((response) => response.json())
.then((json) => {
if (json.next !== null) {
urlQueue.push(json.next)
}
rawResults.push(json.results)
});

}

return rawResults;
}
Insert cell
half_hourly_agile_tariffs = {
const productCode = "AGILE-FLEX-22-11-25";
const tariffCode = `E-1R-${productCode}-${region}`;
const periodTo = half_hourly_meter_consumption[0].validTo.toISOString();
return fetch(
`https://api.octopus.energy/v1/products/${productCode}/electricity-tariffs/${tariffCode}/standard-unit-rates?period_from=${startDate.toISOString()}&period_to=${periodTo}&page_size=25000`
)
.then((response) => response.json())
.then((json) => {
// if (json.next !== null) {
// throw new Error("Response has more values with `next`");
// }

const parsed = json.results.map((r) => {
const validFrom = new Date(r.valid_from);
const validTo = new Date(r.valid_to);

return {
pencePerKwhIncVat: r.value_inc_vat,
validFrom,
validTo
};
});
return parsed;
});
}
Insert cell
Insert cell
mpan = account.properties[0].electricity_meter_points[0].mpan
Insert cell
region = fetch(`https://api.octopus.energy/v1/electricity-meter-points/${mpan}`).then((response) => response.json()).then(json => json.gsp.split("_")[1])
Insert cell
serialNumber = account.properties[0].electricity_meter_points[0].meters[1].serial_number
Insert cell
Insert cell
Insert cell
dayOfYearYear = d3.timeFormat("(%j) %a %e %b '%y")
Insert cell
timeOfDay = d3.timeFormat("%H:%M")
Insert cell
weekOfYearYear = d3.timeFormat("%W-%Y")
Insert cell
extractProductCodeFromTariffCode = (
tariffCode
) =>{
const startIndex = tariffCode.indexOf("E-1R-");
const endIndex = tariffCode.indexOf("-C", startIndex);
if (startIndex !== -1 && endIndex !== -1) {
return tariffCode.substring(startIndex + 5, endIndex);
}
return null;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more