Skip to content

Instantly share code, notes, and snippets.

@cjheath
Last active September 17, 2020 23:09
Show Gist options
  • Save cjheath/bc2425bc11f940222acffe31b2f8d1ad to your computer and use it in GitHub Desktop.
Save cjheath/bc2425bc11f940222acffe31b2f8d1ad to your computer and use it in GitHub Desktop.
#! /bin/bash
# Get electricity price data from Amber, and usage/generation/grid power from a Fronius inverter (site), and report.
#
# (c) Copyright 2020 Clifford Heath.
# Code is known to be incorrect. Free for use at your own risk.
# Per Amber interim API documentation:
# All timestamps in the data are market time (ie Brisbane time) and at period end,
# not start (so if it's 915am, the current period is the 930am one).
# These prices are GST inclusive, so divide by 1.1 if you need them exclusive.
# Usage:
# data.staticPrices.E1.totalfixedKWHPrice + data.staticPrices.E1.lossFactor * data.variablePricesAndRenewables.[period].wholesaleKWHPrice
# And the formula for Export to Grid prices is:
# data.staticPrices.B1.totalfixedKWHPrice - data.staticPrices.B1.lossFactor * data.variablePricesAndRenewables.[period].wholesaleKWHPrice
# Some aspects of the above formulae are uncertain. I'm checking the details with Amber.
#
# Fetch current pricing data from Amber. Substitute your postcode here.
amber_prices=`cat <<COMMAND
curl -s -X POST https://api.amberelectric.com.au/prices/listprices -H 'Content-Type: application/json' -d '{ "postcode": "2087" }' || echo "{}"
COMMAND
`
# Fetch power flow data from a Fronius inverter. Substitute the DNS name or IP address of your inverter:
inverter=inverter
power_flow=`cat <<COMMAND
curl -s 'http://$inverter/solar_api/v1/GetPowerFlowRealtimeData.fcgi' || echo '{}'
COMMAND
`
node <<JAVASCRIPT
// Get price data from Amber:
var prices = `eval $amber_prices`;
if (prices === undefined // Some sanity checking
|| prices['data'] === undefined
|| prices.data['staticPrices'] === undefined
|| prices.data['variablePricesAndRenewables'] === undefined)
{
console.log("Failed to fetch price data");
process.exit(1);
}
// Extract the main structures:
var staticPrices = prices.data.staticPrices;
var variablePricesAndRenewables = prices.data.variablePricesAndRenewables;
// Find the variable price data for the current period:
var now = new Date(); // NOT IMPLEMENTED: Fetch current market time, i.e. Brisbane time (no DST)
var currentVariableDataIndex = variablePricesAndRenewables.findIndex(
variablePricesAndRenewable => {
// the period here is the END of the current half-hour
var period = Date.parse(variablePricesAndRenewable.period);
var is_current_period = (period-30*60*1000 <= now && period > now);
return is_current_period;
}
);
var variablePricesAndRenewable = variablePricesAndRenewables[currentVariableDataIndex];
var totalfixedKWHPriceE1 = +staticPrices["E1"].totalfixedKWHPrice;
var totalfixedKWHPriceB1 = +staticPrices["B1"].totalfixedKWHPrice; // Per JB, this isn't needed, it's a fudge
var wholesaleKWHPrice = +variablePricesAndRenewable.wholesaleKWHPrice;
var lossFactor = +staticPrices["E1"].lossFactor; // The negative of this is copied into B1.
var period = new Date(Date.parse(variablePricesAndRenewable.period));
// Check these two expressions with Amber:
var usage_rate = totalfixedKWHPriceE1 + lossFactor*wholesaleKWHPrice;
var export_rate = /* totalfixedKWHPriceB1 + */ lossFactor*wholesaleKWHPrice;
// Report price data:
var period_representation = period.toLocaleTimeString();
console.log("At " + now.toLocaleString() + " (time period ending " + period_representation + "):");
console.log("\tGrid loss factor is " + Math.round((lossFactor-1)*1000)/10 + '%');
console.log("\tTotal Fixed Price is " + Math.round(totalfixedKWHPriceE1*10)/10 + " c/kWhr");
console.log("\tWholesale Price is " + Math.round(wholesaleKWHPrice*10)/10 + " c/kWhr");
console.log("\tUsage costs " + Math.round(usage_rate*10)/10 + " c/kWhr");
console.log("\tExport earns " + Math.round(export_rate*10)/10 + " c/kWhr");
// Get generation&usagedata from the inverter:
var power_flow = `eval $power_flow`;
if (power_flow["Body"] === undefined)
{
console.log("Failed to fetch generation and usage data");
process.exit(1);
}
var site_power = power_flow["Body"]["Data"]["Site"];
var grid_power = +site_power["P_Grid"]; // +ve = import, -ve = export
var load_power = +site_power["P_Load"]; // Usage
var pv_power = +site_power["P_PV"]; // Generation
console.log("\tUsing " + Math.round(-load_power) + "W, generating " + Math.round(pv_power) + "W");
// Normalise the instantaneous usage rate to a daily cost in cents:
var cents_per_day = (grid_power > 0 ? usage_rate : -export_rate) *grid_power*24/1000;
console.log("Currently "
+ (grid_power > 0 ? "buying " : "selling ")
+ Math.round(Math.abs(grid_power)/100)/10 + "kW"
+ " at $" + Math.round(cents_per_day)/100 + "/day"
);
JAVASCRIPT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment