Octopus Agile - display tariff in graphs & tables, best import/export periods - all done using Node-RED and JSONata

FYI, I’ve changed the trigger time to 20:05 as the last “high export” period commonly finishes around 19:30 and I’d like to be able to see that it’s still active for debugging (which is commonly an evening activity for me.) Looking forward to seeing your up to date flow when you find a way to publish it. In the mean time this is great and I didn’t perceive any end of year bug!

Hi,

I ended up splitting the on/off routine to:-

alias: Octopus Agile On when cheap
description: Switches the charger on as required due to price
trigger:
  - platform: template
    value_template: >
      {% set ns=namespace() %}

      {% set ns.flag=false %}

      {% for event in state_attr('sensor.octopus_agile_sequence_table',
      'import_array')%}
        {% set ontime=(as_timestamp(event.from)|int-as_timestamp(now())|int) %}
        {% set ns.flag = ns.flag or ( 0 < ontime < 120) %}
      {% endfor %}

      {{ns.flag}}
condition: []
action:
  - type: turn_off
    device_id: c6a94c2771b1017d237a2296bdcedfd2
    entity_id: e5459e5851f10461467b65b25e3ff78d
    domain: switch
  - type: turn_off
    device_id: 7edec7d57d25aec2087890f5a5f04a67
    entity_id: 8ebeb8b43536bd1b90b49f4c15c9aeb7
    domain: switch
  - delay:
      hours: 0
      minutes: 1
      seconds: 0
      milliseconds: 0
  - type: turn_on
    device_id: 9b47a99753f04f2492261578d394f039
    entity_id: 1091735bf66ed005ae641aa6e72eb8b2
    domain: switch
  - if:
      - type: is_battery_level
        condition: device
        device_id: 1b2ce1663e3ac6a5d2f0e62b8d31d15a
        entity_id: ba4c9140c65fea26f759bd9ef9709af5
        domain: sensor
        below: 92
      - condition: numeric_state
        entity_id: >-
          sensor.octopus_energy_electricity_22j0694042_1100019822666_current_rate
        below: sensor.current_import_data
        enabled: false
    then:
      - type: turn_on
        device_id: e5cb3e8425f3ee9e4e6fb40b9e6fdf09
        entity_id: 97f67dea5e5ec0468f40f42e13439060
        domain: switch
mode: single

and

alias: Octopus Agile Off when not cheap
description: Switches the charger off as required due to price
trigger:
  - platform: template
    value_template: >
      {% set ns=namespace(flag = false) %}

      {% for event in state_attr('sensor.octopus_agile_sequence_table',
      'import_array')%}
        {% set offtime=(as_timestamp(event.upto)|int-as_timestamp(now())|int) %}
        {% set ns.flag = ns.flag or (0 < offtime < 120) %}
      {% endfor %}

      {{ns.flag}}
condition: []
action:
  - if:
      - condition: device
        type: is_on
        device_id: e5cb3e8425f3ee9e4e6fb40b9e6fdf09
        entity_id: 97f67dea5e5ec0468f40f42e13439060
        domain: switch
    then:
      - type: turn_off
        device_id: e5cb3e8425f3ee9e4e6fb40b9e6fdf09
        entity_id: 97f67dea5e5ec0468f40f42e13439060
        domain: switch
  - type: turn_off
    device_id: 9b47a99753f04f2492261578d394f039
    entity_id: 1091735bf66ed005ae641aa6e72eb8b2
    domain: switch
  - delay:
      hours: 0
      minutes: 0
      seconds: 30
      milliseconds: 0
  - type: turn_on
    device_id: c6a94c2771b1017d237a2296bdcedfd2
    entity_id: e5459e5851f10461467b65b25e3ff78d
    domain: switch
    enabled: false
  - if:
      - condition: numeric_state
        entity_id: >-
          sensor.octopus_energy_electricity_22j0694042_1100019822666_current_rate
        below: 0.99
        above: input_number.locked_in_price
    then:
      - type: turn_on
        device_id: c6a94c2771b1017d237a2296bdcedfd2
        entity_id: e5459e5851f10461467b65b25e3ff78d
        domain: switch
      - type: turn_on
        device_id: 7edec7d57d25aec2087890f5a5f04a67
        entity_id: 8ebeb8b43536bd1b90b49f4c15c9aeb7
        domain: switch

Which controls multiple switches with caveats to charge batteries and protect elements. The offtime on the table works well. The only failure is if the switch drops off the network (on wifi) or if home assistant has a bad day. To try and put these niggles to rest i have 3 asus routers around the house in AIMesh mode and they reboot every 2 or 3 days at 0310 in the morning. This helps keep the network fresh and reduces hiccups with devices dropping off.

1 Like

Nice coding :clap:

I’m finding that the octopus intelligent integration is going offline at random times. I have set up an automation to reload the integration when it goes “offline” or in to an “unknown” state which can work done off the times, but the other times the error log says:

Logger: homeassistant.components.automation.intelligent_reload
Source: components/automation/__init__.py:690
Integration: Automation (documentation, issues)
First occurred: 09:16:14 (3 occurrences)
Last logged: 11:13:32

Error while executing automation automation.intelligent_reload: The config entry (octopus_intelligent) with entry_id 7ecb0d4804d0d6fa151bb98d91e79aca cannot be unloaded because it is not in a recoverable state (ConfigEntryState.FAILED_UNLOAD)

Has anyone else experienced this and has a better work Around? I also checked another friend HA instalation when my intelligent integration was offline in this state, his was still working OK…

I’m trying a different way (I’m not on Agile yet so I can’t use the Octopus sensors that you have). However I’ve run out of coding skill. The data I want does exist and below is what ChatGPT suggests to only run if the average is above 15p/kWh (the minimum average price I want to export) but I’m not convinced the arrays will match in the enumeration - any thoughts please?

EDIT: removed nonworking code

Yeah the arrays don’t match so that didn’t work. Two ways I was overcomplicating

  1. Every day I’ve observed, the higher price peak is in the afternoon, so I can just add a condition to ignore the morning. Only thing is, I’m not sure if this will always be true on sunny days in the summer. Time will tell I guess.
condition:
  - condition: time
    after: "15:00:00"
    before: "19:30:00"
    weekday:
      - mon
      - tue
      - wed
      - thu
      - fri
      - sat
      - sun
  1. We’re already in the export array, so it’s simpler to use the export prices as a proxy for the import prices, and just adjust the trigger event value accordingly, then find the average of the next few events. I think I have an appropriate template with conditions and I’m testing it.

The first code below works, using export array as a proxy for high import price/high carbon to turn off power-intensive device(s), in my case all AC zones.

What mistake have I made with the template below it, which attempts to use the import prices sensor directly? I realise that would trigger for any half-hour period, not for a contiguous block but I would like to experiment with both (rather than deviate from Geoff’s node-RED flow and add a high-import prices block to the array, which would be yet another alternative I may yet pursue. I read that there are limits on array size though…)

The message from Developer Tools: Template for the second set of code is
TypeError: ‘NoneType’ object is not iterable

alias: Agile Export peak above slider value
description: >-
  Agile high price/carbon turn AC off and create notification (test for slider,
  works with value)
trigger:
  - platform: template
    id: "off"
    value_template: >
      {% set ns=namespace() %}

      {% set ns.flag=false %}


      {% for event in state_attr('sensor.octopus_agile_sequence_table_2',
      'export_array') %}
        {% set ontime=(as_timestamp(event.from)|int-as_timestamp(now())|int) %}
        {% if 0 < ontime < 120 and event.average | float >= states('input_number.agile_export_target') | float %}
          {% set ns.flag = true %}
        {% endif %}
      {% endfor %}

      {{ ns.flag }}
condition: []
action:
  - service: climate.set_hvac_mode
    data:
      hvac_mode: "off"
    target:
      entity_id:
        - climate.bedroom_2_c
        - climate.bedroom_3_d
        - climate.downstairs_open_plan
        - climate.master_bedroom_a
        - climate.study_b
  - service: persistent_notification.create
    metadata: {}
    data:
      message: >-
        AC turned off because average for upcoming period exceeded export target
        slider value. Adjust the export target slider if action desired.        
      title: High carbon:AC off!
  - service: notify.whatsapp
    metadata: {}
    data:
      message: >-
        Price/carbon optimisation active: AC off! Average for upcoming period
        exceeded export target slider value. 
mode: single

Note: number helper slider defines the export target in the code above; for testing simplicity it’s hard-coded in the one below.

{% set ns=namespace() %}
{% set ns.flag=false %}

{# We use the calculated high export period to turn *off* some high-current devices, saving money on Agile and/or reducing carbon load by proxy #}
{% for event in state_attr('sensor.octopus_agile_prices_2', 'array') %}
  {% set ontime=(as_timestamp(event.from)|int-as_timestamp(now())|int) %}
  {# Templates run each minute, so allow 60 secs each way, then check if the average is above the target set by a number helper (inconvenient if low impact) #}
  {% if 0 < ontime < 120 and event.import >= 18 %}
    {% set ns.flag = true %}
  {% endif %}
{% endfor %}
{{ ns.flag }}

Edit: corrected the capitalisation of array as per Geoff’s advice

It should probably be ‘array’ and not ‘Array’

Doh! Thanks… :slight_smile: It appears capitalised in the entities view!

I’ve been trying to get this working and im getting an error in the logs and i’m a bit stuck.

When i check the state in the developer tools, it says unavailable, however if i put a debug node on the end of the pricing flow i do see an entity with 96 attributes.

I’m quite new to this - this is my first delve into Node-Red so its quite possible im doing/have done something wrong! So any advice is much appreciated

After a quite a lot of googling, tinkering and trying stuff out, i think i have found the solution. It came down to the sensor configuration. The state class of measurement was the issue, if you clear that down and restart it the problem goes away.

Heres a pic of the config that worked for me;

Alas, my original over-exuberant use of State class ‘measurement’ hit problems last year when the rules were tightened.

Although this was picked up at the time, it was remiss of me not to try to edit my original post (I don’t think I was aware that this was actually possible at the time).

I have now re-loaded the original Node-RED flow (in first post) with slight modifications to clear the offending State Class entries.

1 Like

Hard to believe that it is one year since I first posted this project…

Although I still don’t use Octopus Agile tariff myself, I continue to look at the graphical display of agile prices, and have been tinkering with the code ever since. Last summer I attempted to add DST and local time. I waited until the time-change in October to test it, and I did for the first time in my life get up in the night just to watch the clocks change. The code did, for the most part, actually work.

From discussions on this thread, I have added a binary sensor to act as a switch. Updates to the display (graph, tables) as well as general improvements to the JSONata code itself.

The basic JSON file for the Node-RED flow is now too large to post here. I have finally got round to learning Github and all that entails, so I am now officially an old git. Yet more learning achieved.

So, the latest update is now on Github. I have collated the documentation into the read.me file, and tested the release, so time to commit.

https://github.com/oatybiscuit/Node-RED-HA-Octopus-Agile-JSONata?tab=readme-ov-file#node-red-ha-octopus-agile-jsonata

1 Like

Hi Geoff - I don’t know if you’d prefer this feedback/ask on github or here… but as I’ve already posted about a different issue there, I’m starting here with this one until you say different :slight_smile:
First of all thank you for such an amazing job in terms of intellectual effort AND such helpful writeups and support to people who’ve come unstuck, and all whilst turning down ‘buy me a coffee’ - what an amazing bloke.I am just struggling to get the entity sensor.octopus_agile_sequence_table to display anywhere* and have just discovered the following warnings repeating in nodered:

“Deprecated API warning: Calls to RED.util.evaluateJSONataExpression must include a callback. This will not be optional in Node-RED 4.0. Please identify the node from the following stack and check for an update on npm. If none is available, please notify the node author.”

18/02/2024, 18:08:41msg : string[1378]
</>
“Error: ↵ at Object.evaluateJSONataExpression (/opt/node_modules/@node-red/util/lib/util.js:775:18)↵ at JSONataService.evaluate (/config/node_modules/node-red-contrib-home-assistant-websocket/dist/common/services/JSONataService.js:58:39)↵ at TypedInputService.getValue (/config/node_modules/node-red-contrib-home-assistant-websocket/dist/common/services/TypedInputService.js:58:85)↵ at SensorController.onInput (/config/node_modules/node-red-contrib-home-assistant-websocket/dist/common/controllers/SensorBaseController.js:76:40)↵ at SensorController._InputOutputController_preOnInput (/config/node_modules/node-red-contrib-home-assistant-websocket/dist/common/controllers/InputOutputController.js:62:76)↵ at /opt/node_modules/@node-red/runtime/lib/nodes/Node.js:214:26↵ at Object.trigger (/opt/node_modules/@node-red/util/lib/hooks.js:166:13)↵ at Sensor.Node._emitInput (/opt/node_modules/@node-red/runtime/lib/nodes/Node.js:206:11)↵ at Sensor.Node.emit (/opt/node_modules/…”
</>
18/02/2024, 18:08:42msg : string[270]

Have I broken something or is there a direction of investigation you could recommend please?

*example symptom:

apexcharts-card custom card attempts produce:
Entity not available: sensor.octopus_agile_sequence_table
Entity not available: sensor.octopus_agile_sequence_table

Hi.

I have seen your messages on Github, I’m just not able to respond easily at the moment.

The issue you quote above is most likely due to out of date Websocket nodes. With Node-RED v3+ all JSONata calls now expect a callback function, which was indeed added in the HA Websocket nodes in a recent update. If you are on the latest HA core, the latest NR addon, the latest NR companion integration and the latest Websocket nodes, you should not see this error and it should all work.

I suggest checking your versions. BUT DO BE CAREFUL. There have been several breaking changes across these components, and incompatible updates may break your system.

As a start, I suggest:
Take an inventory of your versions, and ideally save a backup copy and remove your old (this) Node-RED flow completely (delete, restart HA and NR), before installing again afresh. So much has changed since my first flow that it is probably better to start again rather than try and copy over the old flow - but you will need the latest versions.

Backup first!

Thank you Geoff. I backed up this morning, and have inventoried as you kindly suggested:
HA Core 2024.1.6 vs 2024.2.2
NR Addon 17.0.7 - current
NR companion 3.1.2 vs 3.1.3
Websocket nodes 0.55.1 vs 0.63

so you were right on the money with your diagnosis. I disabled config nodes, deleted the whole flow, restarted, re installed, re configured. Whenever I try this I do run into nodered alerting me to pre-existing nodes, and I’ve not yet taught myself how to delete these from homeassistant & nodered successfully.

Having updated websockets I am no longer seeing either of those nodered warnings - so thanks very much.

I can get to green lights on all areas of your flow, working price array and table, but no joy on the sensor.octopus_agile_sequence_table which appears still to not exist. I need to go and re-read your notes in case it needs to wait for cronplus and will magically appear in a few hours.

Anyway - a huge leg up on my journey to assessing solar forecast, battery soc and current load trend and making sensible charging decisions. Really brilliant!

I would expect the sequence table to be updated only with a successful API call after 17:30,and if the raw data is/was missing from the context, no sequence.

As long as the tariff array (96 records) is there in the flow context, you can try using the ‘manual’ inject node in the flow which should trigger a recalc of the computed tables to save you waiting until tomorrow afternoon.

yes I have manual injected numerous times - I always end up with Octopus Agile Input Binary timestamp in NR being updated to now, e.g. ‘green light. false at Feb 18, 22:16’ but no context (if I understand that term correctly) for any of the nodes within the Binary Sensor bottom box of the flow (Binary Sensor - create and manage dynamic switching schedules), whereas I can see context for every node in the upper 3 boxes of the flow. When I go to HA and try to find any sign of the sequence table it seems to not exist.

Update 24 hours later. I’ve finally realised that the friendly name is what is needed to bridge the gap between the table cards and the config nodes in nodered and my config node had nothing in friendly name. Populating that with octopus_agile_sequence_table gives me a sensor entity with visible attributes. Still have a blank card apart from headings but I’m stumbling towards the light :slight_smile:

update some minutes later - now realised that in Homeassistant I am able to change entity id from format nodered_bd4485c035f519e2 to octopus_agile_sequence_table, and this (rather than friendly name) finally sorts my issue.

@NickSee

Hi Nick

I have a bit of time to look at this - do you have any issues remaining or are you now up and running OK?

I am assuming that all the issues you had were down to problems with the sensor configuration nodes.

Hi Geoff, very kind of you to offer your time like that. I am all up and running with the fruits of your work thank you. The only usability issue I have is a very minor one: on PC the Octopus Agile Rates import column is perfectly rendered in green yellow red background with black text pricing. When I use my HA app on android, the same table is rendered with white pricing text which is consequently unreadable. My fault for being on Dark Mode on my Android. Is there any property I could tweak? My googlage hasn’t lead me anywhere productive on that one yet.
I am currently working on templates to do some decision making regarding charge slots, and getting my head around the jsonata logic that produces the import windows. In my use case I would want to make use of the 3 slots my (Growatt) inverter affords me (as opposed to having one long import chunk of time coming out of Octopus Agile Period since I’ve been looking at it) and I currently manually chose the cheapest combination of slots that will get me to a target SoC by the following morning. I mention this only because I’m conscious of all the thought that you have put into your current rules and I am probably duplicating chunks of effort here. I am busy designing ways to achieve balancing consumption profiles with SoC with the output of your sensors and with the solar forecast and enjoying a LOT of learning :slight_smile:

Good to know that it is all working at last.

The agile price table uses some basic html to modify the background colour. I stick to the default light mode, but I would guess that you can modify the foreground also, or just remove that bit of the configuration.

The flow is intended to provide the basic tariff array in context store, and to return the current values for each half-hour. The extra bits are there as a guide as to what can be done, but clearly it is there to be modified for individual use. I am still looking at SOC modelling and finding it very difficult with too many variables and unreliable solar PV forecasting. The current experiment is to capture long term all solar / load figures hourly for a year, then run them through different control models and Octopus tariffs (agile, flux, fixed) to see what happens. I have decided that, if it is viable, I will go for the Flux tariff as it is more predictable and therefore potentially more manageable than Agile.

There is always a lot of learning in this - the JSONata may be rather complicated so I’m happy to assist if you are tinkering with that in particular and need any explanation or help.

Best wishes!