- 
      
- 
        Save yholkamp/8e6af834be1f277536e76dd355d523dd to your computer and use it in GitHub Desktop. 
Revisions
- 
        yholkamp revised this gist Aug 3, 2025 . 1 changed file with 0 additions and 52 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -7,37 +7,6 @@ * Update 2025-02-10: hhi, gecombineerd resultaat voor de bijdragende individuele (Sessy) battterijen, met import/export kWhs en batterij-capaciteit * Update 2025-07-01: hhi, toegevoegd de mogelijkheid om de batterij resultaten te vermenigvuldigen met het aantal deelnemende batterijen * Update 2025-07-03: hhi, automatische bepaling van de handelsmodus en handelsstrategie van de batterij, en deze doorgeven aan Onbalansmarkt.com */ const timeZone = 'Europe/Amsterdam'; @@ -400,29 +369,8 @@ if (handelsmode === "IMBALANCE_TRADING" && handelsstrategie === "STANDARD") { } else { handelsmodus = "manual"; } console.log(`Determined mode: ${handelsmodus}`); // Send the measurement to Onbalansmarkt.com await onbalansmarkt.sendMeasurement({ timestamp: currentTime, 
- 
        yholkamp revised this gist Aug 3, 2025 . 1 changed file with 244 additions and 78 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -1,10 +1,43 @@ /** * JavaScript code om de opbrengst van een batterij naar Onbalansmarkt.com te sturen. * * Auteur: erdebee, met wijzigingen van verschillende gebruikers * Update 2025-01-24: update om impex, Frank Slim korting en totaal handelsresultaat in te sturen. * Update 2025-01-23: update naar netto 'totaal kortingsfactuur' ipv bruto resultaat * Update 2025-02-10: hhi, gecombineerd resultaat voor de bijdragende individuele (Sessy) battterijen, met import/export kWhs en batterij-capaciteit * Update 2025-07-01: hhi, toegevoegd de mogelijkheid om de batterij resultaten te vermenigvuldigen met het aantal deelnemende batterijen * Update 2025-07-03: hhi, automatische bepaling van de handelsmodus en handelsstrategie van de batterij, en deze doorgeven aan Onbalansmarkt.com -SESSIE- <ID-EXAMPLE-OUTPUT => { "data": { "smartBatterySessions": { "deviceId": "---ID---", "fairUsePolicyVerified": false, "periodStartDate": "2025-02-10", "periodEndDate": "2025-02-10", "periodEpexResult": -0.3212414999999999, --> EPEX-correctie € -0,32 "periodFrankSlim": 0.15302999999999994, --> Handelsresultaat.Frank Slim Korting € 0,15 "periodImbalanceResult": 0.4614773533332047, --> ''''.Onbalansresultaat € 0,46 "periodTotalResult": 0.2932658533332047, --> Totaal kortingsfactuur € 0,29 "periodTradeIndex": null, "periodTradingResult": 0.6145073533332046, --> Handelsresultaat € 0,61 "sessions": [ { "cumulativeTradingResult": 0.6145073533332046, "date": "2025-02-10", "tradingResult": 0.6145073533332046, "result": 0.6145073533332046, "status": "ACTIVE", "tradeIndex": null } ], "totalTradingResult": 293.9425048467043 } } } */ const timeZone = 'Europe/Amsterdam'; @@ -63,37 +96,6 @@ class FrankEnergie { return this.auth; } async getSmartBatteries() { if (!this.auth) { @@ -129,29 +131,33 @@ class FrankEnergie { const query = { query: ` query SmartBatterySessions($startDate: String!, $endDate: String!, $deviceId: String!) { smartBatterySessions( startDate: $startDate endDate: $endDate deviceId: $deviceId ) { deviceId fairUsePolicyVerified periodStartDate periodEndDate periodEpexResult periodFrankSlim periodImbalanceResult periodTotalResult periodTradeIndex periodTradingResult sessions { cumulativeTradingResult date tradingResult result status tradeIndex } totalTradingResult } } `, operationName: "SmartBatterySessions", variables: { @@ -164,6 +170,55 @@ class FrankEnergie { return await this.query(query); } async getSmartBattery(deviceId) { if (!this.auth) { throw new Error("Authentication required"); } const query = { query: ` query SmartBattery($deviceId: String!) { smartBattery(deviceId: $deviceId) { brand capacity createdAt externalReference id maxChargePower maxDischargePower provider updatedAt settings { aggressivenessPercentage algorithm batteryMode createdAt effectiveTill imbalanceTradingAggressiveness imbalanceTradingStrategy selfConsumptionTradingAllowed selfConsumptionTradingThresholdPrice tradingAlgorithm updatedAt requestedUpdate { aggressivenessPercentage batteryMode effectiveFrom imbalanceTradingStrategy } } } } `, operationName: "SmartBattery", variables: { deviceId } }; return await this.query(query); } isAuthenticated() { return this.auth !== null; } @@ -188,13 +243,15 @@ class OnbalansMarkt { chargerResult = null, batteryResultEpex = null, batteryResultImbalance = null, batteryResultCustom = null, mode = 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(), @@ -209,7 +266,8 @@ class OnbalansMarkt { ...(chargerResult !== null && { chargerResult: chargerResult.toString() }), ...(batteryResultEpex !== null && { batteryResultEpex: batteryResultEpex.toString() }), ...(batteryResultImbalance !== null && { batteryResultImbalance: batteryResultImbalance.toString() }), ...(batteryResultCustom !== null && { batteryResultCustom: batteryResultCustom.toString() }), ...(mode !== null && { mode: mode.toString() }) }; try { @@ -235,10 +293,36 @@ class OnbalansMarkt { } } class HomeyVars { async getVariableValue(name, defaultValue) { const variable = await global.get(name); return variable !== undefined ? variable : defaultValue; } async setVariableValue(name, value) { await tag(name, value); await global.set(name, value); } } const homeyVars = new HomeyVars(); // Lees de variabelen const frankenergie_pw = await homeyVars.getVariableValue('frankenergie_pw', 'defaultPassword'); const frankenergie_id = await homeyVars.getVariableValue('frankenergie_id', 'defaultEmail'); const onbalansmarkt_apikey = await homeyVars.getVariableValue('onbalansmarkt_apikey', 'defaultApiKey'); // Log de waarden van de variabelen console.log(`Frank Energie Password: ${frankenergie_pw}`); console.log(`Frank Energie Login: ${frankenergie_id}`); console.log(`Onbalansmarkt API Key: ${onbalansmarkt_apikey}`); const frank = new FrankEnergie(); await frank.login(frankenergie_id, frankenergie_pw); const onbalansmarkt = new OnbalansMarkt(onbalansmarkt_apikey); // Get all smart batteries const batteries = await frank.getSmartBatteries(); @@ -247,28 +331,110 @@ const batteries = await frank.getSmartBatteries(); let currentTime = new Date(); // wanneer je de opgenomen en geleverde kWhs beschikbaar hebt van je batterij, dan kun je die hier ophalen en aan onderstaande variabelen toewijzen. // wat betreft de kwhCharged en kwhDischarged variabelen, deze is nu een gemiddelde over de gehele set van batterijen. // we moeten deze waarde nog vermenigvuldigen met het aantal deelnemende (en daardoor bijdragende) batterijen let kwhCharged = await homeyVars.getVariableValue('deltaImportPower', null); let kwhDischarged = await homeyVars.getVariableValue('deltaExportPower', null); let battCharged = await homeyVars.getVariableValue('averageBatteryLevel', null); // Get sessions for a specific battery // Accumulate results let accumulatedPeriodTotalResult = 0; let accumulatedTotalTradingResult = 0; let accumulatedPeriodEpexResult = 0; let accumulatedPeriodTradingResult = 0; let accumulatedPeriodFrankSlim = 0; // Initialize the loop counter let loopCounter = 0; // Get sessions for each battery for (const battery of batteries.data.smartBatteries) { // Increment the loop counter for each battery loopCounter++; const batteryId = battery.id; const sessions = await frank.getSmartBatterySessions( batteryId, currentTime, currentTime ); console.log("-SESSIE-", batteryId, "=>", JSON.stringify(sessions, null, 2)); accumulatedPeriodTotalResult += sessions.data.smartBatterySessions.periodTotalResult; accumulatedTotalTradingResult += sessions.data.smartBatterySessions.totalTradingResult; accumulatedPeriodEpexResult += sessions.data.smartBatterySessions.periodEpexResult; accumulatedPeriodTradingResult += sessions.data.smartBatterySessions.periodTradingResult; accumulatedPeriodFrankSlim += sessions.data.smartBatterySessions.periodFrankSlim; } // Multiply kwhCharged and kwhDischarged by the loop counter, needed to get the accumulated charged and discharged kWhs if (kwhCharged !== null) { kwhCharged *= loopCounter; console.log(`kwhCharged over alle deelnemende batterijen: ${kwhCharged}`); } if (kwhDischarged !== null) { kwhDischarged *= loopCounter; console.log(`kwhDischarged over alle deelnemende batterijen: ${kwhDischarged}`); } //aanroep van getBattery, om de handelsmode ne handelstrategie van de batterij op te halen const battery = await frank.getSmartBattery(batteries.data.smartBatteries[0].id); console.log("-BATTERY- ", JSON.stringify(battery) ); const handelsmode = battery.data.smartBattery.settings.batteryMode console.log(`Handelsmode: ${handelsmode}`) const handelsstrategie = battery.data.smartBattery.settings.imbalanceTradingStrategy console.log(`Handelsstrategie: ${handelsstrategie}`) // bepaal welke handelsmodus we gaan gebruiken, afhankelijk van de handelsmode en handelsstrategie van de batterij let handelsmodus = "imbalance"; //default modus is imbalance if (handelsmode === "IMBALANCE_TRADING" && handelsstrategie === "STANDARD") { handelsmodus = "imbalance"; } else if (handelsmode === "IMBALANCE_TRADING" && handelsstrategie === "AGGRESSIVE") { handelsmodus = "imbalance_aggressive"; } else if (handelsmode === "SELF_CONSUMPTION_MIX") { handelsmodus = "self_consumption_plus"; } else { handelsmodus = "manual"; } console.log(`Determined mode: ${handelsmodus}`); // Example output for the measurement to be sent to Onbalansmarkt.com // { // "timestamp": "2025-01-20T16:00:00Z", // "batteryResult": "8.80", // "batteryResultTotal": "1004.75", // "batteryResultEpex": "-1.40", // "batteryResultImbalance": "9.60", // "batteryResultCustom": "0.60", // "batteryCharge": "76", // "batteryPower": "-9000", // "chargedToday": "11", // "dischargedToday": "8", // "loadBalancingActive": "on", (on/off) // "solarResult": "string", // "chargerResult": "string", // "totalBatteryCycles": "143", // "mode": "imbalance" (imbalance/imbalance_aggressive/manual/day_ahead/self_consumption/self_consumption_plus) // } // Send the measurement to Onbalansmarkt.com await onbalansmarkt.sendMeasurement({ timestamp: currentTime, batteryResult: accumulatedPeriodTotalResult, batteryResultTotal: accumulatedTotalTradingResult, batteryCharge: battCharged, 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, batteryResultEpex: accumulatedPeriodEpexResult, batteryResultImbalance: accumulatedPeriodTradingResult, batteryResultCustom: accumulatedPeriodFrankSlim, mode: handelsmodus, }); 
- 
        yholkamp revised this gist Jan 30, 2025 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -17,6 +17,7 @@ class FrankEnergie { async query(queryData) { const headers = { 'Content-Type': 'application/json', 'User-Agent': 'Homey/FrankV1', ...(this.auth && { 'Authorization': `Bearer ${this.auth.authToken}` }) }; 
- 
        yholkamp revised this gist Jan 24, 2025 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,7 @@ /** * JavaScript code om de opbrengst van een batterij naar Onbalansmarkt.com te sturen. * * Update 2025-01-24: update om impex, Frank Slim korting en totaal handelsresultaat in te sturen. * Update 2025-01-23: update naar netto 'totaal kortingsfactuur' ipv bruto resultaat * * Auteur: erdebee, met wijzigingen van verschillende gebruikers 
- 
        yholkamp revised this gist Jan 24, 2025 . 1 changed file with 15 additions and 7 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ /** * JavaScript code om de opbrengst van een batterij naar Onbalansmarkt.com te sturen. * * Update 2025-01-23: update naar netto 'totaal kortingsfactuur' ipv bruto resultaat * * Auteur: erdebee, met wijzigingen van verschillende gebruikers @@ -183,7 +183,10 @@ class OnbalansMarkt { dischargedToday = null, loadBalancingActive = null, solarResult = null, chargerResult = null, batteryResultEpex = null, batteryResultImbalance = null, batteryResultCustom = null }) { // Validate required fields if (!timestamp || !batteryResult || !batteryResultTotal) { @@ -201,7 +204,10 @@ class OnbalansMarkt { ...(dischargedToday !== null && { dischargedToday: dischargedToday.toString() }), ...(loadBalancingActive !== null && { loadBalancingActive: loadBalancingActive.toString() }), ...(solarResult !== null && { solarResult: solarResult.toString() }), ...(chargerResult !== null && { chargerResult: chargerResult.toString() }), ...(batteryResultEpex !== null && { batteryResultEpex: batteryResultEpex.toString() }), ...(batteryResultImbalance !== null && { batteryResultImbalance: batteryResultImbalance.toString() }), ...(batteryResultCustom !== null && { batteryResultCustom: batteryResultCustom.toString() }) }; try { @@ -238,7 +244,7 @@ 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 ophalen en aan onderstaande variabelen toewijzen. let kwhCharged = null; let kwhDischarged = null; @@ -250,14 +256,16 @@ if (batteries.data.smartBatteries.length > 0) { currentTime, currentTime ); await onbalansmarkt.sendMeasurement({ timestamp: currentTime, batteryResult: sessions.data.smartBatterySessions.periodTotalResult, 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, batteryResultEpex: sessions.data.smartBatterySessions.periodEpexResult, batteryResultImbalance: sessions.data.smartBatterySessions.periodTradingResult, batteryResultCustom: sessions.data.smartBatterySessions.periodFrankSlim }); } else { console.log("No batteries found"); 
- 
        yholkamp revised this gist Jan 23, 2025 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,7 @@ /** * JavaScript code om de opbrengst van een batterij naar Onbalansmarkt.com te sturen. * * Update 2025-01-23: update naar netto 'totaal kortingsfactuur' ipv bruto resultaat * * Auteur: erdebee, met wijzigingen van verschillende gebruikers */ 
- 
        yholkamp revised this gist Jan 23, 2025 . 1 changed file with 1 addition and 2 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -132,15 +132,14 @@ class FrankEnergie { deviceId: $deviceId ) { deviceId periodStartDate periodEndDate periodEpexResult periodFrankSlim periodImbalanceResult periodTotalResult periodTradeIndex periodTradingResult sessions { cumulativeTradingResult date 
- 
        yholkamp revised this gist Jan 23, 2025 . 1 changed file with 7 additions and 1 deletion.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -133,7 +133,13 @@ class FrankEnergie { ) { deviceId periodEndDate periodEpexResult periodFrankSlim periodImbalanceResult periodStartDate periodTotalResult periodTradeIndex periodTradingResult periodTradingResult sessions { cumulativeTradingResult @@ -246,7 +252,7 @@ if (batteries.data.smartBatteries.length > 0) { console.log(sessions); onbalansmarkt.sendMeasurement({ timestamp: currentTime, batteryResult: sessions.data.smartBatterySessions.periodTotalResult, 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, 
- 
        yholkamp revised this gist Jan 10, 2025 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -231,7 +231,8 @@ 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 ophalen en insturen let kwhCharged = null; let kwhDischarged = null; // Get sessions for a specific battery 
- 
        yholkamp revised this gist Jan 5, 2025 . 1 changed file with 171 additions and 162 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -1,62 +1,67 @@ /** * 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 @@ -76,23 +81,23 @@ class FrankEnergie { } } `, 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 @@ -107,19 +112,19 @@ class FrankEnergie { } } `, 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 @@ -139,80 +144,80 @@ class FrankEnergie { } } `, 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(); @@ -222,26 +227,30 @@ 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"); } 
- 
        erdebee revised this gist Dec 10, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -207,7 +207,7 @@ class OnbalansMarkt { throw new Error(`HTTP error! status: ${response.status}`); } return await response.text(); } catch (error) { console.error('Error sending measurement:', error); throw error; 
- 
        erdebee revised this gist Dec 10, 2024 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -142,8 +142,8 @@ class FrankEnergie { operationName: "SmartBatterySessions", variables: { deviceId, startDate: new Date(startDate.toLocaleString('en-US', { timeZone })).toISOString().split('T')[0], endDate: new Date(endDate.toLocaleString('en-US', { timeZone })).toISOString().split('T')[0] } }; 
- 
        erdebee revised this gist Dec 10, 2024 . 1 changed file with 4 additions and 2 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ const timeZone = 'Europe/Amsterdam'; class FrankEnergie { constructor(authToken = null, refreshToken = null) { this.DATA_URL = "https://frank-graphql-prod.graphcdn.app/"; @@ -75,8 +77,8 @@ class FrankEnergie { } `, 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" }; 
- 
        erdebee revised this gist Dec 10, 2024 . 1 changed file with 5 additions and 5 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -162,7 +162,7 @@ class OnbalansMarkt { async sendMeasurement({ timestamp, batteryResult, batteryResultTotal, batteryCharge = null, batteryPower = null, deliveryToday = null, @@ -172,15 +172,15 @@ class OnbalansMarkt { 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() }), ...(deliveryToday !== null && { deliveryToday: deliveryToday.toString() }), @@ -236,7 +236,7 @@ if (batteries.data.smartBatteries.length > 0) { ); console.log(sessions); onbalansmarkt.sendMeasurement({ timestamp: yesterday, batteryResult: sessions.data.smartBatterySessions.periodTradingResult, batteryResultTotal: sessions.data.smartBatterySessions.totalTradingResult, loadBalancingActive: "off" 
- 
        erdebee revised this gist Dec 10, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -178,7 +178,7 @@ class OnbalansMarkt { // Prepare the payload const payload = { timestamp: timestamp.toISOString(), batteryResult: batteryResult.toString(), ...(batteryResultTotal !== null && { batteryResultTotal: batteryResultTotal.toString() }), ...(batteryCharge !== null && { batteryCharge: batteryCharge.toString() }), 
- 
        erdebee revised this gist Dec 10, 2024 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -238,6 +238,7 @@ if (batteries.data.smartBatteries.length > 0) { onbalansmarkt.sendMeasurement({ timestamp: yesterday.toISOString(), batteryResult: sessions.data.smartBatterySessions.periodTradingResult, batteryResultTotal: sessions.data.smartBatterySessions.totalTradingResult, loadBalancingActive: "off" }); } 
- 
        erdebee revised this gist Dec 10, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -236,7 +236,7 @@ if (batteries.data.smartBatteries.length > 0) { ); console.log(sessions); onbalansmarkt.sendMeasurement({ timestamp: yesterday.toISOString(), batteryResult: sessions.data.smartBatterySessions.periodTradingResult, loadBalancingActive: "off" }); 
- 
        erdebee created this gist Dec 8, 2024 .There are no files selected for viewingThis 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,244 @@ 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: startDate.toISOString().split('T')[0], endDate: endDate.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.toISOString().split('T')[0], endDate: endDate.toISOString().split('T')[0] } }; return await this.query(query); } isAuthenticated() { return this.auth !== null; } } class OnbalansMarkt { constructor(apiKey) { this.apiUrl = 'https://onbalansmarkt.com/nexus/api/live'; this.apiKey = apiKey; } async sendMeasurement({ timestamp, batteryResult, batteryResultTotal = null, batteryCharge = null, batteryPower = null, deliveryToday = null, productionToday = null, loadBalancingActive = null, solarResult = null, chargerResult = null }) { // Validate required fields if (!timestamp || !batteryResult) { throw new Error('timestamp and batteryResult are required fields'); } // Prepare the payload const payload = { timestamp, batteryResult: batteryResult.toString(), ...(batteryResultTotal !== null && { batteryResultTotal: batteryResultTotal.toString() }), ...(batteryCharge !== null && { batteryCharge: batteryCharge.toString() }), ...(batteryPower !== null && { batteryPower: batteryPower.toString() }), ...(deliveryToday !== null && { deliveryToday: deliveryToday.toString() }), ...(productionToday !== null && { productionToday: productionToday.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.json(); } 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(); console.log(batteries); let yesterday = new Date(); yesterday.setHours(0,0,0,0); yesterday.setDate(yesterday.getDate() - 1); // 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, yesterday, yesterday ); console.log(sessions); onbalansmarkt.sendMeasurement({ timestamp: new Date().toISOString(), batteryResult: sessions.data.smartBatterySessions.periodTradingResult, loadBalancingActive: "off" }); }