Skip to content

Instantly share code, notes, and snippets.

@yholkamp
Forked from erdebee/frank-onbalansmarkt.js
Last active August 3, 2025 13:25
Show Gist options
  • Save yholkamp/8e6af834be1f277536e76dd355d523dd to your computer and use it in GitHub Desktop.
Save yholkamp/8e6af834be1f277536e76dd355d523dd to your computer and use it in GitHub Desktop.
Post daily frank energie trading results to onbalansmarkt
/**
* JavaScript code om de opbrengst van een batterij naar Onbalansmarkt.com te sturen.
*
* Auteur: erdebee, met wijzigingen van verschillende gebruikers
*/
const timeZone = 'Europe/Amsterdam';
class FrankEnergie {
constructor(authToken = null, refreshToken = null) {
this.DATA_URL = "https://frank-graphql-prod.graphcdn.app/";
this.auth = authToken || refreshToken ? { authToken, refreshToken } : null;
}
async query(queryData) {
const headers = {
'Content-Type': 'application/json',
...(this.auth && { 'Authorization': `Bearer ${this.auth.authToken}` })
};
try {
const response = await fetch(this.DATA_URL, {
method: 'POST',
headers,
body: JSON.stringify(queryData)
});
const data = await response.json();
if (data.errors) {
for (const error of data.errors) {
if (error.message === "user-error:auth-not-authorised") {
throw new Error("Authentication required");
}
}
}
return data;
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
async login(username, password) {
const query = {
query: `
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
authToken
refreshToken
}
}
`,
operationName: "Login",
variables: { email: username, password }
};
const response = await this.query(query);
this.auth = response.data.login;
return this.auth;
}
async getPrices(startDate, endDate) {
const query = {
query: `
query MarketPrices($startDate: Date!, $endDate: Date!) {
marketPricesElectricity(startDate: $startDate, endDate: $endDate) {
from
till
marketPrice
marketPriceTax
sourcingMarkupPrice
energyTaxPrice
}
marketPricesGas(startDate: $startDate, endDate: $endDate) {
from
till
marketPrice
marketPriceTax
sourcingMarkupPrice
energyTaxPrice
}
}
`,
variables: {
startDate: new Date(startDate.toLocaleString('en-US', { timeZone })).toISOString().split('T')[0],
endDate: new Date(endDate.toLocaleString('en-US', { timeZone })).toISOString().split('T')[0]
},
operationName: "MarketPrices"
};
return await this.query(query);
}
async getSmartBatteries() {
if (!this.auth) {
throw new Error("Authentication required");
}
const query = {
query: `
query SmartBatteries {
smartBatteries {
brand
capacity
createdAt
externalReference
id
maxChargePower
maxDischargePower
provider
updatedAt
}
}
`,
operationName: "SmartBatteries"
};
return await this.query(query);
}
async getSmartBatterySessions(deviceId, startDate, endDate) {
if (!this.auth) {
throw new Error("Authentication required");
}
const query = {
query: `
query SmartBatterySessions($startDate: String!, $endDate: String!, $deviceId: String!) {
smartBatterySessions(
startDate: $startDate
endDate: $endDate
deviceId: $deviceId
) {
deviceId
periodEndDate
periodStartDate
periodTradingResult
sessions {
cumulativeTradingResult
date
tradingResult
}
totalTradingResult
}
}
`,
operationName: "SmartBatterySessions",
variables: {
deviceId,
startDate: startDate.toLocaleDateString('en-CA', { timeZone: timeZone }), // specificeer de lokale datum in YYYY-mm-dd formaat
endDate: endDate.toLocaleDateString('en-CA', { timeZone: timeZone })
}
};
return await this.query(query);
}
isAuthenticated() {
return this.auth !== null;
}
}
class OnbalansMarkt {
constructor(apiKey) {
this.apiUrl = 'https://onbalansmarkt.com/api/live';
this.apiKey = apiKey;
}
async sendMeasurement({
timestamp,
batteryResult,
batteryResultTotal,
batteryCharge = null,
batteryPower = null,
chargedToday = null,
dischargedToday = null,
loadBalancingActive = null,
solarResult = null,
chargerResult = null
}) {
// Validate required fields
if (!timestamp || !batteryResult || !batteryResultTotal) {
throw new Error('timestamp, batteryResult and batteryResultTotal are required fields');
}
// Prepare the payload
const payload = {
timestamp: timestamp.toISOString(),
batteryResult: batteryResult.toString(),
batteryResultTotal: batteryResultTotal.toString(),
...(batteryCharge !== null && { batteryCharge: batteryCharge.toString() }),
...(batteryPower !== null && { batteryPower: batteryPower.toString() }),
...(chargedToday !== null && { chargedToday: chargedToday.toString() }),
...(dischargedToday !== null && { dischargedToday: dischargedToday.toString() }),
...(loadBalancingActive !== null && { loadBalancingActive: loadBalancingActive.toString() }),
...(solarResult !== null && { solarResult: solarResult.toString() }),
...(chargerResult !== null && { chargerResult: chargerResult.toString() })
};
try {
const response = await fetch(this.apiUrl, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
} catch (error) {
console.error('Error sending measurement:', error);
throw error;
}
}
}
const frank = new FrankEnergie();
await frank.login("[email protected]", "mijnfrankpassword");
const onbalansmarkt = new OnbalansMarkt("API-KEY-ONBALANSMARKT.COM");
// Get all smart batteries
const batteries = await frank.getSmartBatteries();
// we sturen met dit script de opbrengst van de batterij, op de huidige tijd, naar Onbalansmarkt.com
let currentTime = new Date();
// wanneer je de opgenomen en geleverde kWhs beschikbaar hebt van je batterij, dan kun je die hier opha
let kwhDischarged = null;
// Get sessions for a specific battery
if (batteries.data.smartBatteries.length > 0) {
const batteryId = batteries.data.smartBatteries[0].id;
const sessions = await frank.getSmartBatterySessions(
batteryId,
currentTime,
currentTime
);
console.log(sessions);
onbalansmarkt.sendMeasurement({
timestamp: currentTime,
batteryResult: sessions.data.smartBatterySessions.periodTradingResult,
batteryResultTotal: sessions.data.smartBatterySessions.totalTradingResult,
loadBalancingActive: "off", // stuur hier enkel 'on' in wanneer de batterij op dit moment beperkt is in zijn vermogen door load balancing
chargedToday: kwhCharged !== null ? Math.round(kwhCharged) : null,
dischargedToday: kwhDischarged !== null ? Math.round(kwhDischarged) : null
});
} else {
console.log("No batteries found");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment