Octopus Energy Saving Sessions

This flow finds and automatically joins Octopus Energy Saving Sessions.

You can also use it to carry out actions when a saving session starts or ends, via the switch node.

Enter your Account Number and API Key (found here) into the Octopus Energy GraphQL node properties (or set a default in the sub flow env properties).

Requires: node-red-contrib-graphql

If you haven't already joined Octopus Energy, seize the opportunity through my referral link https://share.octopus.energy/blue-beach-240. By using this link, both of us will receive a £50 credit. Already an Octopus Energy Customer? You can still add the referral code by contacting them, as long as you signed up directly without any referral code. Let's both enjoy a £50 credit!

[{"id":"8c10a679bfd2386e","type":"subflow","name":"Octopus Energy GraphQL","info":"","category":"","in":[{"x":80,"y":140,"wires":[{"id":"ab8b6368f466dd5a"}]}],"out":[{"x":770,"y":140,"wires":[{"id":"799223ca9ce61160","port":0}]},{"x":480,"y":580,"wires":[{"id":"4c8b3548cc35074b","port":0}]}],"env":[{"name":"ACCOUNTNUMBER","type":"str","value":"","ui":{"icon":"font-awesome/fa-address-card-o","label":{"en-US":"Account Number"},"type":"input","opts":{"types":["str"]}}},{"name":"APIKEY","type":"cred","ui":{"icon":"font-awesome/fa-key","label":{"en-US":"API Key"},"type":"input","opts":{"types":["str","cred"]}}}],"meta":{},"credentials":{"APIKEY":""},"color":"#D8BFD8","inputLabels":["GraphQL query"],"outputLabels":["GraphQL output","GraphQL error"],"status":{"x":440,"y":840,"wires":[{"id":"6c5a11d5aa1aa28e","port":0}]}},{"id":"89c2bb38024725e7","type":"graphql","z":"8c10a679bfd2386e","name":"obtainKrakenToken","graphql":"920ed6a5c25f475c","format":"text","template":"mutation krakenTokenAuthentication($apikey: String!) {\n  obtainKrakenToken(input: {APIKey: $apikey}) {\n    token\n  }\n}","syntax":"plain","token":"","showDebug":false,"x":480,"y":400,"wires":[["9e176a124108b191"],["178a5d28b4fe2aab"]]},{"id":"45fc3f5515b6658b","type":"function","z":"8c10a679bfd2386e","name":"Set API Key","func":"if (!msg.variables) {\n    msg.variables = {};\n}\n\nmsg.variables.apikey = env.get(\"APIKEY\")\n\nmsg.payload={}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":230,"y":400,"wires":[["89c2bb38024725e7"]]},{"id":"9e176a124108b191","type":"function","z":"8c10a679bfd2386e","name":"Set krakenToken flow variable","func":"flow.set(\"octopus.graphql.krakenToken\", msg.payload.graphql.obtainKrakenToken.token)\n\nif(msg.refreshToken) {\n    delete msg.refreshToken\n}\n\ndelete msg.payload.graphql\n\nvar status = { fill: \"yellow\", text: \"Token obtained\" }\n\nreturn [msg, status]","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":400,"wires":[["b22be99526d4a842"],["b5f209b32c478859"]],"outputLabels":["Query","Status"]},{"id":"799223ca9ce61160","type":"graphql","z":"8c10a679bfd2386e","name":"Run GraphQL","graphql":"920ed6a5c25f475c","format":"handlebars","template":"{{query}}","syntax":"mustache","token":"","showDebug":false,"x":520,"y":140,"wires":[["beb4a5a15a77d3df"],["07b8cede1bb55008"]]},{"id":"ab8b6368f466dd5a","type":"function","z":"8c10a679bfd2386e","name":"Prepare for GraphQL","func":"var status\n\nif (!flow.get(\"octopus.graphql.krakenToken\")) {\n    var newMsg = {}\n    newMsg.refreshToken = true\n    newMsg.oldMsg = msg\n\n    status = { fill: \"purple\", text: \"Obtaining token\" }\n\n    return [null, newMsg, status]\n}\n\nif(msg.oldMsg) {\n    msg = msg.oldMsg\n}\n\nmsg.customHeaders = {}\nmsg.customHeaders.Authorization = flow.get(\"octopus.graphql.krakenToken\")\n\nif (!msg.variables) {\n    msg.variables = {};\n}\n\nmsg.variables.accountNumber = env.get(\"ACCOUNTNUMBER\")\n\nif(!msg.payload) {\n    msg.payload = {}\n}\nmsg.payload.graphql={}\n\nstatus = { fill: \"yellow\", text: \"Running query\" }\n\nreturn [msg, null, status];","outputs":3,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":140,"wires":[["799223ca9ce61160"],["da8e4250054dc797"],["bad44dd40a123091"]],"inputLabels":["Query"],"outputLabels":["GraphQL","Get Token","Status"]},{"id":"be9497eea5625c3f","type":"link in","z":"8c10a679bfd2386e","name":"Obtain Token","links":["436150eaf14baa43","da8e4250054dc797"],"x":75,"y":400,"wires":[["45fc3f5515b6658b"]]},{"id":"da8e4250054dc797","type":"link out","z":"8c10a679bfd2386e","name":"Get Token","mode":"link","links":["be9497eea5625c3f"],"x":455,"y":200,"wires":[]},{"id":"b22be99526d4a842","type":"link out","z":"8c10a679bfd2386e","name":"Run query","mode":"link","links":["d9312dbf0a913258"],"x":1005,"y":400,"wires":[]},{"id":"d9312dbf0a913258","type":"link in","z":"8c10a679bfd2386e","name":"Run query","links":["b22be99526d4a842"],"x":75,"y":200,"wires":[["ab8b6368f466dd5a"]]},{"id":"4c8b3548cc35074b","type":"function","z":"8c10a679bfd2386e","name":"Handle errors","func":"var status\n\nif (msg.payload.graphql[0].extensions) {\n    if (msg.payload.graphql[0].extensions.errorCode == \"KT-CT-1124\") {\n        // token expired\n        var newMsg = {}\n        newMsg.refreshToken = true\n        newMsg.oldMsg = msg\n        status = { fill: \"yellow\", text: \"Obtaining new token\" }\n        return [null, newMsg, status]\n    }\n    else {\n        // other octopus error\n        node.warn(msg.payload.graphql[0].extensions)\n        status = { fill: \"red\", text: msg.payload.graphql[0].extensions.errorCode }\n    }\n}\nelse {\n    // general graphql error\n    node.warn(msg.payload.graphql.error)\n    status = { fill: \"red\", text: msg.payload.graphql.error.message}\n}\n\nreturn [msg, null, status];","outputs":3,"noerr":0,"initialize":"","finalize":"","libs":[],"x":240,"y":580,"wires":[[],["436150eaf14baa43"],["b53b2d031b1bac91"]],"outputLabels":["GraphQL Error","Refresh token","Status"]},{"id":"436150eaf14baa43","type":"link out","z":"8c10a679bfd2386e","name":"Token expired","mode":"link","links":["be9497eea5625c3f"],"x":425,"y":640,"wires":[]},{"id":"35bd2d5bcfdc2443","type":"link in","z":"8c10a679bfd2386e","name":"Status","links":["57e7f83e79d3234d","a2a109ed52baf28c","b53b2d031b1bac91","b5f209b32c478859","bad44dd40a123091"],"x":75,"y":840,"wires":[["6c5a11d5aa1aa28e"]]},{"id":"178a5d28b4fe2aab","type":"link out","z":"8c10a679bfd2386e","name":"GraphQL error","mode":"link","links":["fefc9c5225e27ee3"],"x":675,"y":460,"wires":[]},{"id":"fefc9c5225e27ee3","type":"link in","z":"8c10a679bfd2386e","name":"Handle errors","links":["07b8cede1bb55008","178a5d28b4fe2aab"],"x":75,"y":580,"wires":[["4c8b3548cc35074b"]]},{"id":"07b8cede1bb55008","type":"link out","z":"8c10a679bfd2386e","name":"GraphQL error","mode":"link","links":["fefc9c5225e27ee3"],"x":695,"y":260,"wires":[]},{"id":"9076d5177c29523e","type":"comment","z":"8c10a679bfd2386e","name":"Run GraphQL query","info":"","x":160,"y":80,"wires":[]},{"id":"f9d2645c6b0a51be","type":"comment","z":"8c10a679bfd2386e","name":"Obtain Kraken Token","info":"","x":160,"y":340,"wires":[]},{"id":"7a7f7676f803a447","type":"comment","z":"8c10a679bfd2386e","name":"Error handling","info":"","x":140,"y":520,"wires":[]},{"id":"b53b2d031b1bac91","type":"link out","z":"8c10a679bfd2386e","name":"Set Status 4","mode":"link","links":["35bd2d5bcfdc2443"],"x":425,"y":700,"wires":[]},{"id":"b5f209b32c478859","type":"link out","z":"8c10a679bfd2386e","name":"Set Status 3","mode":"link","links":["35bd2d5bcfdc2443"],"x":1005,"y":460,"wires":[]},{"id":"bad44dd40a123091","type":"link out","z":"8c10a679bfd2386e","name":"Set Status 1","mode":"link","links":["35bd2d5bcfdc2443"],"x":455,"y":260,"wires":[]},{"id":"beb4a5a15a77d3df","type":"function","z":"8c10a679bfd2386e","name":"Success status","func":"var status = { fill: \"green\", text: \"Success\" }\n\nreturn status;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":200,"wires":[["a2a109ed52baf28c"]]},{"id":"a2a109ed52baf28c","type":"link out","z":"8c10a679bfd2386e","name":"Set Status 2","mode":"link","links":["35bd2d5bcfdc2443"],"x":905,"y":200,"wires":[]},{"id":"60e116b69cbdd166","type":"inject","z":"8c10a679bfd2386e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":900,"wires":[["f28df1509b4cbacc"]]},{"id":"f28df1509b4cbacc","type":"function","z":"8c10a679bfd2386e","name":"Initial status","func":"\nreturn ({fill: \"blue\", text: \"Ready\"});","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":900,"wires":[["57e7f83e79d3234d"]]},{"id":"57e7f83e79d3234d","type":"link out","z":"8c10a679bfd2386e","name":"Set Status 5","mode":"link","links":["35bd2d5bcfdc2443"],"x":445,"y":900,"wires":[]},{"id":"6c5a11d5aa1aa28e","type":"function","z":"8c10a679bfd2386e","name":"Send status","func":"return {payload: msg};","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":840,"wires":[[]]},{"id":"5046bcad1a0540e3","type":"comment","z":"8c10a679bfd2386e","name":"Status","info":"","x":110,"y":780,"wires":[]},{"id":"920ed6a5c25f475c","type":"graphql-server","name":"Octopus API","endpoint":"https://api.octopus.energy/v1/graphql/","token":""},{"id":"669992652ac08f13","type":"tab","label":"Flow 5","disabled":false,"info":"","env":[]},{"id":"3e4f0f69bffe32c1","type":"template","z":"669992652ac08f13","name":"Get Saving Session info","field":"query","fieldType":"msg","format":"text","syntax":"plain","template":"query savingSessionInfo($accountNumber: String!) {\n  savingSessions(accountNumber: $accountNumber) {\n    account(accountNumber: $accountNumber) {\n      joinedEvents {\n        status\n        startAt\n        endAt\n        eventId\n        rewardGivenInOctoPoints\n        energySavedInKwh\n        baselineConsumptionDeltaKwh\n        consumptionDeltaKwh\n        netReductionPctRank\n        percentageSaved\n      }\n    }\n    events {\n      code\n      startAt\n      endAt\n      id\n      rewardPerKwhInOctoPoints\n      totalParticipants\n    }\n  }\n}","output":"str","x":370,"y":100,"wires":[["542fdd1d8798e46f"]]},{"id":"30acccbe7ecc9bf7","type":"debug","z":"669992652ac08f13","name":"GraphQL Error","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":220,"y":360,"wires":[]},{"id":"d8ec12ba637d29ea","type":"function","z":"669992652ac08f13","name":"Process data","func":"const currentTime = new Date()\n\nvar upcomingJoinedEvents = []\nvar eventsToJoin = []\n\nvar statusMsg = {}\nstatusMsg.is_saving_session = false\n\nfor (let index in msg.payload.graphql.savingSessions.account.joinedEvents) {\n    let joinedEvent = msg.payload.graphql.savingSessions.account.joinedEvents[index]\n    if (joinedEvent.status == \"UPCOMING\") {\n        upcomingJoinedEvents.push(joinedEvent.id)\n\n        let start = new Date(joinedEvent.startAt)\n        let end = new Date(joinedEvent.endAt)\n        \n        if (start <= currentTime && currentTime < end) {\n            statusMsg.is_saving_session = true\n            statusMsg.event = joinedEvent\n        }\n    }\n}\n\nfor (let index in msg.payload.graphql.savingSessions.events) {\n    let event = msg.payload.graphql.savingSessions.events[index]\n    let start = new Date(event.startAt)\n    \n    if (start > currentTime && !upcomingJoinedEvents.includes(event.id)) {\n        eventsToJoin.push({ variables: { eventCode: event.code }, event: event})\n    }\n}\n\n\nreturn [eventsToJoin, statusMsg];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":900,"y":100,"wires":[["e37cae2aa44de953"],["bc77702054e107e7"]],"outputLabels":["Events to join","Saving Session status"]},{"id":"936da848bcf0295b","type":"template","z":"669992652ac08f13","name":"Join Saving Session","field":"query","fieldType":"msg","format":"text","syntax":"plain","template":"mutation MyMutation($accountNumber: String!, $eventCode: String!) {\n  joinSavingSessionsEvent(\n    input: {accountNumber: $accountNumber, eventCode: $eventCode}\n  ) {\n    joinedEventCodes\n  }\n}","output":"str","x":380,"y":240,"wires":[["c1987a7d6434a482"]]},{"id":"e37cae2aa44de953","type":"link out","z":"669992652ac08f13","name":"Join Event","mode":"link","links":["df327e523960ebaa"],"x":1075,"y":100,"wires":[]},{"id":"df327e523960ebaa","type":"link in","z":"669992652ac08f13","name":"Join event","links":["e37cae2aa44de953"],"x":75,"y":240,"wires":[["936da848bcf0295b"]]},{"id":"2a11c9d0c6ede173","type":"cronplus","z":"669992652ac08f13","name":"Every 30 mins","outputField":"payload","timeZone":"","storeName":"","commandResponseMsgOutput":"output1","defaultLocation":"","defaultLocationType":"default","outputs":1,"options":[{"name":"Schedule","topic":"topic1","payloadType":"default","payload":"","expressionType":"cron","expression":"*/30 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"}],"x":140,"y":100,"wires":[["3e4f0f69bffe32c1"]]},{"id":"5bccc3537aa819d3","type":"function","z":"669992652ac08f13","name":"Prepare notification","func":"let start = new Date(msg.event.startAt)\nlet end = new Date(msg.event.endAt)\n\nvar newMsg = {}\nnewMsg.topic = \"Joined Octopus Saving Session\"\nnewMsg.sound = \"cashregister\"\nnewMsg.payload = start.toDateString() + \" \" + start.toLocaleTimeString() + \" - \" + end.toLocaleTimeString() + \" \\n\" + msg.event.rewardPerKwhInOctoPoints + \" points per Kwh\"\nreturn newMsg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":920,"y":240,"wires":[["37827af75dcea516"]]},{"id":"37827af75dcea516","type":"pushover","z":"669992652ac08f13","name":"","device":"","title":"","priority":0,"sound":"","url":"","url_title":"","html":false,"x":1140,"y":240,"wires":[]},{"id":"542fdd1d8798e46f","type":"subflow:8c10a679bfd2386e","z":"669992652ac08f13","name":"","x":640,"y":100,"wires":[["d8ec12ba637d29ea"],["5e4bcdf438802a2c"]]},{"id":"c1987a7d6434a482","type":"subflow:8c10a679bfd2386e","z":"669992652ac08f13","name":"","x":640,"y":240,"wires":[["5bccc3537aa819d3"],["668a0e1ddb696859"]]},{"id":"bc77702054e107e7","type":"link out","z":"669992652ac08f13","name":"Saving Session status","mode":"link","links":["baf377b3fc2d4ccb"],"x":1075,"y":140,"wires":[]},{"id":"baf377b3fc2d4ccb","type":"link in","z":"669992652ac08f13","name":"Saving Session status","links":["bc77702054e107e7"],"x":75,"y":480,"wires":[["74252578b1c97c0a"]]},{"id":"75001674c4d10204","type":"switch","z":"669992652ac08f13","name":"Saving Session (Start/End)","property":"is_saving_session","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":480,"y":480,"wires":[[],[]],"outputLabels":["Saving Session Started","Saving Session Ended"]},{"id":"74252578b1c97c0a","type":"rbe","z":"669992652ac08f13","name":"State change","func":"rbe","gap":"","start":"","inout":"out","septopics":false,"property":"is_saving_session","topi":"topic","x":220,"y":480,"wires":[["75001674c4d10204"]]},{"id":"5ae0a70ae8bd4599","type":"comment","z":"669992652ac08f13","name":"Saving Session Start/End","info":"","x":170,"y":440,"wires":[]},{"id":"5e4bcdf438802a2c","type":"link out","z":"669992652ac08f13","name":"GraphQL error 1","mode":"link","links":["2c96e06a442e8e73"],"x":835,"y":140,"wires":[]},{"id":"668a0e1ddb696859","type":"link out","z":"669992652ac08f13","name":"GraphQL error 2","mode":"link","links":["2c96e06a442e8e73"],"x":835,"y":280,"wires":[]},{"id":"2c96e06a442e8e73","type":"link in","z":"669992652ac08f13","name":"GraphQL error","links":["5e4bcdf438802a2c","668a0e1ddb696859"],"x":75,"y":360,"wires":[["30acccbe7ecc9bf7"]]},{"id":"d475f13acacc35c9","type":"comment","z":"669992652ac08f13","name":"GraphQL error debug","info":"","x":160,"y":320,"wires":[]},{"id":"f91f724766790b34","type":"comment","z":"669992652ac08f13","name":"Join Saving Session","info":"","x":160,"y":200,"wires":[]},{"id":"7b2b6d5fb83db01c","type":"comment","z":"669992652ac08f13","name":"Check for Saving Sessions","info":"","x":180,"y":60,"wires":[]}]

Flow Info

Created 4 months, 3 weeks ago
Rating: 5 2

Owner

Actions

Rate:

Node Types

Core
  • comment (x8)
  • debug (x1)
  • function (x9)
  • inject (x1)
  • link in (x7)
  • link out (x14)
  • rbe (x1)
  • switch (x1)
  • template (x2)
Other

Tags

  • octopus
  • octopus-energy
  • graphql
  • saving-session
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option