Logo
  • Batterijen
  • Live
  • ProfielRegistreren
Homey
  1. Home
  2. Handleidingen
  3. Homey
  4. Resultaten van Frank Energie doorsturen via Homey

Resultaten van Frank Energie doorsturen via Homey

Wil je jouw resultaten live delen op deze site zodat we de opbrengst kunnen vergelijken? Meld je dan aan, voeg je installatie toe en log in. Je hebt dan twee keuzes:

  1. Stel een directe koppeling in: hierbij worden je resultaten automatisch uitgelezen en gepubliceerd. Dit loopt dan niet via Homey.
  2. Lees de gegevens van Frank Energie en je batterij uit met Homey en gebruik de API key van je profielpagina om de gegevens te delen, hoe je dit doet leggen we op deze pagina uit.

Configuratie in Homey

We nemen aan dat je een Homey account en een Homey of Homey Pro hebt.

Optie 1: gebruik de (beta-)app

'hhi' heeft een app ontwikkeld die de gegevens van Frank Energie kan uitlezen en beschikbaar maakt in Homey en ook gelijk kan delen met Mijnbatterij. Je vindt de app hier bij Homey.

Optie 2: configureer Homey met HomeyScript

  1. Installeer HomeyScript, hiermee kun je scripts installeren op je Homey.
  2. Vervolgens heb je twee mogelijkheden, op deze pagina beschrijven we een eenvoudig script dat de basisfuncties biedt voor systemen met één batterij.
    Ben je op zoek naar een uitgebreide koppeling voor SolarEdge, AlphaESS of heb je meer dan één batterij in je Frank Energie account? Dan raden we aan om verder te gaan met deze uitgebreidere handleiding, ontwikkeld door 'hhi'. De configuratie hiervan is iets ingewikkelder, maar zorgt er wel voor dat ook de geladen en ontladen stroom goed doorgestuurd wordt.
  3. Voeg voor de basis-koppeling het volgende script toe aan je Homey configuratie:
/**
 * JavaScript code om de opbrengst van een batterij naar Mijnbatterij 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 Mijnbatterij
 */
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',
      'User-Agent': 'Homey/FrankV1',
      ...(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 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
				fairUsePolicyVerified
                periodStartDate
                periodEndDate
                periodEpexResult
                periodFrankSlim
                periodImbalanceResult
                periodTotalResult
                periodTradeIndex
                periodTradingResult
                sessions {
                    cumulativeTradingResult
                    date
                    tradingResult
					result
					status
					tradeIndex
                }
                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);
  }
  
  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;
  }
}

class OnbalansMarkt {
  constructor(apiKey) {
    this.apiUrl = 'https://api.mijnbatterij.nl/api/live';
    this.apiKey = apiKey;
  }
  
  async sendMeasurement({
                          timestamp,
                          batteryResult,
                          batteryResultTotal,
                          batteryCharge = null,
                          batteryPower = null,
                          chargedToday = null,
                          dischargedToday = null,
                          loadBalancingActive = null,
                          solarResult = null,
                          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(),
      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() }),
      ...(batteryResultEpex !== null && { batteryResultEpex: batteryResultEpex.toString() }),
      ...(batteryResultImbalance !== null && { batteryResultImbalance: batteryResultImbalance.toString() }),
      ...(batteryResultCustom !== null && { batteryResultCustom: batteryResultCustom.toString() }),
      ...(mode !== null && { mode: mode.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;
    }
  }
}

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();

// we sturen met dit script de opbrengst van de batterij, op de huidige tijd, naar Mijnbatterij
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}`);

// Send the measurement to Mijnbatterij
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, 
});
  1. Voeg een nieuwe 'Flow' toe en begin met een tijd-stap die 5 minuten draait.
  2. Laat vervolgens deze stap het script uitvoeren.
  3. Stel de volgende variabelen in:
    • frankenergie_id: Vul hier je Frank Energie e-mailadres in.
    • frankenergie_pw: Vul hier je Frank Energie wachtwoord in.
    • onbalansmarkt_apikey: Plak hier je API key van je profielpagina.
  4. Ter controle, test de flow een keer om de werking te controleren.
Avatar 1Avatar 2Avatar 3

Data van 400+ echte gebruikers

Heb je al een batterij?

Deel je data
Logo

Ontdek wat huishoudens in Nederland echt verdienen of besparen met een thuisbatterij - geen commerciële belangen, alleen feiten.

info@mijnbatterij.nl

Arthurweg 61
3077 NW Rotterdam

Data
  • Live resultaten
  • Dagresultaten
  • Maandresultaten
  • Jaarresultaten
  • Batterijen
  • Installaties
Meer
  • Achtergrond
  • Veelgestelde vragen
  • Handleidingen
  • Keuzehulp
  • Controleer mijn offerte
Functies
  • Blog & updates
  • Contact & over ons
  • Samenwerken
  • Fout melden

© 2026 Mijnbatterij. Alle rechten voorbehouden.

PrivacyAlgemene voorwaarden
LinkedInBluesky