Last active
September 17, 2020 23:09
-
-
Save cjheath/bc2425bc11f940222acffe31b2f8d1ad to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /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