[APP][DEV][PRO] Octopus Energy Integration

V1.0.38 Released

Add capability to the Energy Account device that, for accounts with Smart Devices, tracks how much of the (forthcoming) dispatch limit of 360 minutes has been consumed. If the account has more than one Smart Device then the dispatch consumption of all Smart Devices is summed. There are no Trigger Flow Cards defined for the capability in this version. The 360 minute limit is currently hardcoded.

Release Usage Analysis

Version Installs Crashes Crash Types Comment
1.0.38 30 98 1 Crash started 14:51 on 11th January
1.0.35 1 1 1 Strange crash - keeping it under review
1.0.30 2 0 0 Robust for non-intelligent tariff customers
1.0.29 1 1 1 OK so far…
1.0.28 1 1 1 Query returning malformed response
1.0.9 1 48390 6 Early release with simplistic assumptions
Totals 36 :scream: 10

71 Kraken Devices

@ 2026-01-11 17:25

A new defect just appeared in 1.0.38. See post at bottom.

Wow this is really great work, so glad I found it ! thank-you @David_Piper for investing such considerable time and effort in to this integration.

Am also running Homey Self Hosted with Home Mini.
Am also on Flexible Tracker tariff (SILVER-24-12-31)

1 Like

Thanks, Darren. Appreciate the kind sentiments.

New Defect Issue #14

Evening all. A new defect has appeared in the App log. It first appeared at 14:51 today 11th January. It’s associated with Smart Energy device (so must be Smart Tariff user). It’s occurring pretty frequently - every 2 or 3 minutes since it started. If you have noticed the app isn’t working for you then please let me know here or by PM so I can work out what is going wrong.

Given that the release has been clean until today and that, according to the App log, there is a new installation out there somewhere, I guess the two things are related.

I will review the code and see if anything obvious leaps out.

Hope to hear from you soon.

V1.0.39 Released

This is a point release to apply a fix for issue #14. This is not a full resolution since I do not understand the root cause of the defect; it should allow the app to operate without errors. It is possible that the smartDevice instance that has been triggering the defect will never display any updates to its capabilities.

If you are seeing that a smartDevice instance is not showing updates to its capability values please get in touch so I can more fully analyse the defect and create a fuller resolution. Either reply here or use a PM.

The defect appears to be related to issue #12. The problem manifests in the same way (but may have a different root cause).

Please upgrade to the new version as soon as you are able.

Release Usage Analysis

Version Installs Crashes Crash Types Comment
1.0.39 32 0 0 Published 2026-01-11 17:25
1.0.38 3 246 1 Affected user has upgraded 2026-01-11 20:23
1.0.30 2 0 0 Robust for non-intelligent tariff customers
1.0.29 1 60 2 OK so far…
1.0.28 1 1 1 Query returning malformed response
Older 1 Lots 6 Stopped explicit monitoring, it’s too old :smiling_face_with_sunglasses:
Totals 40 307 4

81 Kraken Devices

@ 2026-01-29 14:00

Looks like a new device has been added to the remaining 1.0.29 installation and this is crashing frequently. Please can I ask that you upgrade from 1.0.29.

Homey Self-Hosted Server

Have a re-purposed ancient windows laptop on which I have installed Zorin to re-vivify the machine. It has a really noisy fan and the battery is dead, but plugged into the mains it does perfectly well as a really simple server on the home network.

This evening I finally got around to installing the Homey Self-Hosted server. Added the Kraken app and created my devices. Also added Tado and my Air Pump. Now I have an alternative environment where I can test the app while I keep my Homey Pro for “production” use. Haven’t decided whether to add the inverter, Solar PV and battery yet…

Bill to pay in 30 days. Hey ho.

Can I use a Zigbee dongle, I wonder…

You can’t use ZigBee dongles with Homey SHS.

If you only use it for testing/development, you could just re-install it every 30 days for another “free trial” :wink:

1 Like

Yeah. Worked that one out…

Hmm might try that. Or just move everything on to SHS with a Bridge and then decide what to do with my Homey Pro. Though, to be fair, the ex-Windoze machine is flakey enough that I shall continue to use it while I can but fully expect it to die sometime soon. Plans for a Ugreen NAS soon - I see that SHS now has an official install for Ugreen. So it’s all up in the air for now.

I’ve just got my Homey and have been testing this out… seems great.

I’m on Intelligent Go, and it would be great to have some more cards available for a flow, such as “dispatching now”, “Next dispatch countdown” and “Next planned start”.

Keep up the good work and I’m happy to help where I can, I have an Octopus Mini.

1 Like

Thanks for the kind comments @Peter85 - they are appreciated. Welcome to the app. Like the suggestions for flow cards - the more suggestions the better the app will be.

It’s going to take a while, busy trying to iron out the Octopus Account device for now. Some of the data it’s showing is just wrong. Hey ho. Opportunity to simplify and improve.

You’ve done an excellent job. This development was the push that made me pick Homey over HA. I’ve maintained HA before but I wanted something simpler and more UX friendly for the family to use.

If I can be of any assistance for testing or whatnot, just let me know.

1 Like

Started using this app, been awaiting a while for an app to be built.
I had written a script to just pull the data for the api for both Electric and Gas as I wanted to have the running daily cost. I see in your description you say gas doesn’t have live data. It only gets updated every 30mins by the meter which is due to the meter being battery powered, but for gas it is sufficient for most users as gas it not running constantly like electric.

Do you have plans to add Gas data in later?

I had also been using MQTT from a HID from Glowmarkt before I had the mini. The HID didn’t give the correct tariff info as not able to handle the flexible slots with Go.

Keep up the good work!

Thanks @Patrick_UK , appreciate the kind sentiments.

I have been pondering “the gas thang” since I started doing the work. I do not have a gas tariff myself and AFAIK the OHM does not support data for gas - although I have found some information to contradict this. Certainly the Kraken API calls that I have identified that return the live data provided by the OHM do not include gas data.

If there is support from OHM for gas data then a Gas tariff device can be added to the app - even if it processes data at a lower frequency (30 minutes).

If there is no OHM support then we are dependent on the batch update from the UK Smart Meter network. That typically happens early afternoon the following day, but can be delayed by another day or even two. We are then faced with the conundrum that Homey does not allow one to say when a value was measured - the measurements are always recorded as “NOW”. Our data is between 12 and 36 hours old (or even more) and it applies to 24 different hours (48 half hours), but it’s all going to be logged in Homey as current data.

I have reviewed the Python code in the HA implementation. As far as I can see the only access to gas data is via the simpler REST api. The REST api does not return live data (at least for Electricity). Based on this I don’t believe that HA reports live Gas consumption data.

Do you see Gas data for “today” in the Octopus App - perhaps every 30 minutes?

Any information you can give me will be very useful.

@David_Piper Yes I can see the gas data in the Octopus app for today. There is a delay which can vary anything upto a few hours.

Below is the script I use to pull the data I wanted, but might give some pointers:

// --- User Configuration ---

const API_KEY = 'XXXXXXX';




// INTELLIGENT ELECTRICITY SETTINGS

const ELEC_RATE_PEAK = 29.2;       // DAY rate (pence/kWh)

const ELEC_RATE_OFFPEAK = 7.0;     // NIGHT rate (pence/kWh)

const ELEC_STANDING_CHARGE = 47.7; // Daily standing charge (pence)




// Off-Peak Window (23:30 to 05:30)

const OFFPEAK_START_HOUR = 23;

const OFFPEAK_START_MIN = 30;

const OFFPEAK_END_HOUR = 5;

const OFFPEAK_END_MIN = 30;




// GAS SETTINGS

const GAS_PRICE_PER_KWH = 6.22;     

const GAS_STANDING_CHARGE = 32.71; 

const GAS_CALORIFIC_VALUE = 39.3;




// SYSTEM

const GRAPHQL_ENDPOINT = 'https://api.octopus.energy/v1/graphql/';




async function run() {

  console.log('--- Starting Octopus Intelligent Tariff Fetch ---');




try {

const token = await getKrakenToken(API_KEY);

const accountNumber = await getAccountNumber(token);

const now = new Date();

const startOfDay = new Date(now);

    startOfDay.setHours(0, 0, 0, 0);




// --- ELECTRICITY ---

    console.log('\n--- Electricity ---');

const elecDeviceId = await getDeviceId(token, accountNumber, 'electricity');

if (elecDeviceId) {

// Live Power (Watts is already correct, no division needed)

const oneMinuteAgo = new Date(now.getTime() - 60 * 1000);

const liveData = await getLiveTelemetry(token, elecDeviceId, oneMinuteAgo.toISOString(), now.toISOString());

if (liveData && liveData.length > 0) {

const latest = liveData[liveData.length - 1];

const watts = parseInt(latest.demand) || 0;

        console.log(`Live Demand: ${watts} W`);

await tag('Octopus Live Power', watts);

      }




// Daily Cost

const dailyData = await getHalfHourlyTelemetry(token, elecDeviceId, startOfDay.toISOString(), now.toISOString());

let totalElecKwh = 0;

let totalElecCostPence = ELEC_STANDING_CHARGE; 




if (dailyData) {

for (const slot of dailyData) {

// FIX: Divide by 1000 to convert Wh to kWh

const kwh = (parseFloat(slot.consumptionDelta) || 0) / 1000;

const readTime = new Date(slot.readAt); 

          totalElecKwh += kwh;




if (isOffPeak(readTime)) {

            totalElecCostPence += (kwh * ELEC_RATE_OFFPEAK);

          } else {

            totalElecCostPence += (kwh * ELEC_RATE_PEAK);

          }

        }

      }

const elecCostPounds = totalElecCostPence / 100;

      console.log(`Daily Usage: ${totalElecKwh.toFixed(2)} kWh`);

      console.log(`Daily Cost (Est): ÂŁ${elecCostPounds.toFixed(2)}`);




await tag('Octopus Elec Daily kWh', parseFloat(totalElecKwh.toFixed(2)));

await tag('Octopus Elec Daily Cost', parseFloat(elecCostPounds.toFixed(2)));

    }




// --- GAS ---

    console.log('\n--- Gas ---');

const gasDeviceId = await getDeviceId(token, accountNumber, 'gas');

if (gasDeviceId) {

const dailyGasData = await getHalfHourlyTelemetry(token, gasDeviceId, startOfDay.toISOString(), now.toISOString());

let totalGasVolume = 0;

if (dailyGasData) {

// FIX: Divide by 10000 to convert Raw Units (Liters) to m3

        totalGasVolume = dailyGasData.reduce((acc, cur) => acc + ((parseFloat(cur.consumptionDelta) || 0) / 10000), 0);

      }

const totalGasKwh = (totalGasVolume * GAS_CALORIFIC_VALUE * 1.02264) / 3.6;

const gasCostPence = (totalGasKwh * GAS_PRICE_PER_KWH) + GAS_STANDING_CHARGE;

const gasCostPounds = gasCostPence / 100;




      console.log(`Daily Usage: ${totalGasKwh.toFixed(2)} kWh`);

      console.log(`Daily Cost: ÂŁ${gasCostPounds.toFixed(2)}`);

await tag('Octopus Gas Daily kWh', parseFloat(totalGasKwh.toFixed(2)));

await tag('Octopus Gas Daily Cost', parseFloat(gasCostPounds.toFixed(2)));

    }




    console.log('--- Script Finished Successfully ---');




  } catch (error) {

    console.error('SCRIPT FAILED:', error.message);

  }

}




// --- Helper Functions ---




function isOffPeak(dateObj) {

const h = dateObj.getHours();

const m = dateObj.getMinutes();

const timeVal = h + (m / 60); 

const startVal = OFFPEAK_START_HOUR + (OFFPEAK_START_MIN / 60);

const endVal = OFFPEAK_END_HOUR + (OFFPEAK_END_MIN / 60);




if (startVal > endVal) {

return (timeVal >= startVal) || (timeVal < endVal);

  } else {

return (timeVal >= startVal) && (timeVal < endVal);

  }

}




async function fetchGraphQL(token, query, variables = {}) {

const headers = { 'Content-Type': 'application/json' };

if (token) headers['Authorization'] = token;

const response = await fetch(GRAPHQL_ENDPOINT, {

    method: 'POST',

    headers: headers,

    body: JSON.stringify({ query, variables }),

  });

if (!response.ok) {

const txt = await response.text(); 

throw new Error(`HTTP Error: ${response.status} - ${txt}`);

  }

const json = await response.json();

if (json.errors) throw new Error(`GraphQL Error: ${JSON.stringify(json.errors)}`);

return json.data;

}




async function getKrakenToken(apiKey) {

const query = `mutation ObtainToken($apiKey: String!) { obtainKrakenToken(input: {APIKey: $apiKey}) { token } }`;

const data = await fetchGraphQL(null, query, { apiKey });

return data.obtainKrakenToken.token;

}




async function getAccountNumber(token) {

const query = `query { viewer { accounts { number } } }`;

const data = await fetchGraphQL(token, query);

if (data.viewer.accounts && data.viewer.accounts.length > 0) return data.viewer.accounts[0].number;

throw new Error('Could not discover account number.');

}




async function getDeviceId(token, accountNumber, fuelType) {

const fieldName = fuelType === 'electricity' ? 'electricityAgreements' : 'gasAgreements';

const query = `query GetAccount($accountNumber: String!) { account(accountNumber: $accountNumber) { ${fieldName}(active: true) { meterPoint { meters(includeInactive: false) { smartDevices { deviceId } } } } } }`;

const data = await fetchGraphQL(token, query, { accountNumber });

const agreements = data.account[fieldName];

if (!agreements) return null;

for (const agreement of agreements) {

if (agreement.meterPoint && agreement.meterPoint.meters) {

for (const meter of agreement.meterPoint.meters) {

if (meter.smartDevices && meter.smartDevices.length > 0) return meter.smartDevices[0].deviceId;

      }

    }

  }

return null;

}




async function getLiveTelemetry(token, deviceId, start, end) {

const query = `query LiveTelemetry($deviceId: String!, $start: DateTime!, $end: DateTime!) { smartMeterTelemetry(deviceId: $deviceId, grouping: TEN_SECONDS, start: $start, end: $end) { readAt, demand, consumptionDelta } }`;

const data = await fetchGraphQL(token, query, { deviceId, start, end });

return data.smartMeterTelemetry;

}




async function getHalfHourlyTelemetry(token, deviceId, start, end) {

const query = `query HistoryTelemetry($deviceId: String!, $start: DateTime!, $end: DateTime!) { smartMeterTelemetry(deviceId: $deviceId, grouping: HALF_HOURLY, start: $start, end: $end) { readAt, consumptionDelta } }`;

const data = await fetchGraphQL(token, query, { deviceId, start, end });

return data.smartMeterTelemetry;

}




await run();

Hi Patrick

Arghhh. What a dummy I am. You think you know the system really well, then you miss the chuffin’ obvious. Thanks to your script I now understand “pass a gas meter id into SmartMeterTelemetry and you get gas meter data back”. DOH! My mistake was thinking of SmartMeterTelemetry as “reading the OHM” - in reality it reads the data related to a specific meter from the OHM.

My concern is your comment:

This may limit the utility of the data because the “NOW-centricity” of Homey.

Glad I could help.

Sounds like Homey have built it based on electricity mainly and maybe for the BeNe region as that is where they are based. So could be their gas meters are able to provide live data. Hopefully this is something they will address in a future update as they expand on the energy dashboard.

For my use case the delay I can live with for the gas.

In the Netherlands, the gas meters are connected wirelessly to the electricity meter and updates every 15 minutes. So we don’t have live updates for gas meters in the Netherlands.

1 Like

Progress Update

It’s been a bit quiet round here recently - but that does not signify a lack of work developing the app. A lot of testing has been undertaken by @Stu_F for the Intelligent Go Tariff. This has exposed a number of defects in the EnergyAccount and SmartEnergyDevice devices. Work to correct these is making good progress and I hope to make a new release this weekend.

A number of capabilities will change location from one device type to another. The most important of these is probably the Energy Price Quartile capability. That will move from the ImportTariff device to the EnergyAccount device. The associated When flow card will move with it. This change may break some of your flows. Apologies.

In addition, for Intelligent Go tariffs the SmartEnergyDevice will change to reference Planned dispatch times rather than extended dispatch times. The In Dispatch capability will mean that the device is receiving energy (not that energy is being discounted). The EnergyAccount device will monitor dispatch reduced prices (over extended dispatch times) in the new Dispatch Pricing capability.

1 Like

Progress Update 2

Overnight testing has worked well, so I can move on to test the EnergyAccount device with Dispatches.

I have become very disenchanted with the performance of the ENUM ui for date selection. I have found that just opening the Capabilities Viewer in the Android app generates multiple changes (often as many as 3) to the value of the Period Start Day. This seems to be an artefact of the way the list is built and displayed in the UI. At least one WHEN flow card is going to look at the value of the Period Start Day - just imagine the chaos that could be caused by triggering a flow with erroneous value 3 times quickly in a row.

I plan on evaluating the use of a slider control in place of the enum. I will do that with a test capability so it should not delay the timing of the next version.

Hi, this being a very active thread about Octopus Energy integration, I would like to know how you are getting your spot prices today?

If you are interested in a script that would produce 30-min day ahead prices starting from “now”, I had a go with Octopus Agile and managed to create a price fetcher that will look ahead by a maximum of 7 hours due to the way the market updates are provided (4pm).