Octopus Fan Club support
Discussed in https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/discussions/538
Originally posted by h4rryb November 17, 2023 I've just installed this - great set of tools to use on HA! I wondered if there is a way to get the current estimated % saving on the electricity unit rate from the Octopus Fan Club in a similar manner to how it shows in the Octopus dashboard (if that info is accessible via the APIs?)
https://www.octopusenergygeneration.com/fan-club/
See original discussion for plan of action, along with some API examples.
Yep this would be very useful for me to schedule charging of the battery. If we could have the future prediction information at the bottom of the page as well that would be great.
Fellow Fan #1 member here! I would love to see this implemented within the integration.
Update for future self or anyone else who wants to tackle it. I think there are three parts to this ticket, but they don't all need to be provided at once and can be split into separate feature requests. Each part should be at the very least it's own pull request.
- Provide previous, current and next discount sensors.
- These sensors should work in a similar way to the previous, current and next rate sensors in that they should a) Combine continous discounts of the same value when calculating the start/end attributes (e.g. if you had a discount of 50% from 10:00-10:30 and 10:30-11:00 and 11:00-11:30, then the start time should be 10:00 and the end time should be 11:30) and b) The previous and next discount should be the previous and next discount that are different to the current discount, not just the next period.
- The above sensors should exist for each fan club the user is a member of
- The above sensors should rely on function(s) for calculating the previous, current and next discount in a similar way to the rate sensors, and these function(s) should be unit tested
- Data for these sensors should be provided via a coordinator, which should refresh every 30 minutes. The coordinator should have the same fallback logic as other coordinators and should be unit tested where possible.
- Docs should be updated to explain these sensors. This should be a new page within the docs. FAQ should be updated with data frequency
- Data diagnostic sensor should be created to indicate when the data was last retrieved.
- Update rates coordinator to apply discount
- The rates coordinator should be updated to apply any applicable discount to an applicable rate (e.g. if an rate is 0.50 and a discount for that period is 50%, then the rate should be updated to be 0.25).
- If a discount is applied, the rate entry should have a new field called
applied_fan_club_discountwith the discount value that was applied so it's clear why the rate might look different to what OE report. - This should be similar to how intelligent rates are adjusted outside of the normal period
- The coordinator changes should be fully unit tested.
- Docs should be updated to reflect new field.
- Update previous consumption cost to apply discount
- The previous consumption cost calculations should be updated to take fan club discounts into account. Because this data is retrieved outside of the normal rates coordinator, its best to tackle this separately.
- The rates breakdowns should make it clear where a discount was applied (same
applied_fan_club_discountfield) - This should be unit tested.
I've quite a novice so first I've taught myself how to interact with the Graph QL API. I've succeeded doing this directly but am yet to figure out how to do this with python. If anyone is working on this in the background, I'd be happy to test code.
this would be very useful
I found some time to put into this, here is my progress so far.
To start with I wanted to learn how to extract information from the API query using yaml, this is simply because I am more familiar with yaml.
I currently have 4x sensors which extract the following information;
- Authorization Sensor - To get an auth token from the API to use in the following sensors. I realise this is already part of the OE integration but I have no idea how to access it.
- Current Discount Sensor - I started with this as it provides the simplest response from the API. This sensor provides the current level of discount. Possible responses are 0, 0.2, 0.5. This is the unaveraged data, 'live' from the turbine.
- Historic Discount Sensor - Queries for historic fan club discount data and timestamps (the call is not limited to just this information). The API responds with the last 47 data points. This sensor provides the actual discount applied to your account within the given time periods. I reached out to Octopus, they said "We calculate mean power values over 30 minute periods (to match up with the 30minute consumption data we get with smart meters). ...we also include the "historic" component which is the 30 minute average already computed (so you don't have to!)."
Sensor 2 (non-averaged discount data):
Sensor 3 (actual discount applied, note the graphed data does not take into account the discount timestamp):
Sensor 3 (data processed with Apex Charts to show the correct discount at the correct timestamp, as seen in the Octopus Energy Dashboard)
Next is to look at forecast data and then figure out how to convert everything to Python so it can be merged with the OE integration.
Some really interesting meta data can be accessed using the API, including wind speed, wind direction and raw kW output.
I was thinking at looking at the first stage of this myself within the next couple of weeks. But as you've started, I'll back off and await your PR :)
@BenGlossop09 Did you get any further with bringing this into this integration?
I got stumped with bringing this into the integration. It's just above my abilities right now. I started by trying to figure out how to structure a python script & reference the authorisation token, I probably should've started by learning the language first. I ended up working on some bug fixes on the yaml instead.
I won't be offended if you (or anyone else) want to progress this.
No problem at all. I'll probably look at getting the first phase in and do a beta release within the next couple of weeks.
FYI the API return for the 'forecast' request will be 'null' if the current output of the turbine is <=0kW. See below for more detail;
"We set the forecast to null if the current output power of the turbine is less than or equal to 0. Because the forecasts are based solely on wind-speed (and we fit it to essentially a power-output curve), the forecasts are, in a way, independent of the status of the turbine. Which is bad. We encoded this logic to prevent us sending out 50% discount notifications (and to show very promising forecasts) in the case where there is an issue with the turbine. e.g. the turbine might be down for preventative maintenance but the forecasts were projecting it to be blowing a gale. We base the "is turbine having difficulty" decision on power <= 0, because we don't have access to a "turbine status" component, or equivalent, in any of our source APIs."
There is a beta release for the fan club sensors available at https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/releases/tag/v15.1.0-beta.1. This has been worked on a little blind as I only had one set of data to go off. I didn't notice the above comment, so this will cause an issue which I'll fix in the next beta release.
The available sensors can be found at https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/blob/feat/fan-club-discount/_docs/entities/fan_club.md and work in the same way as the rate sensors do.
Thanks for putting the time into that @BottlecapDave
Looks like the current discount sensor is generally working well however something seems to have occurred around 6am. Octopus reports 20% between 0530-0700. The HA sensor reports the same (0526-0656) except for between 0600-0626 where it reports 0% - see below.
The next and previous discount sensors don't seem to be updating frequently enough. Example below of the previous discount sensor being very out of date.
The data updates every 30 minutes to try and keep data requests low, so there's a chance it uses a predicted percentage when the site has the actual percentage. The gaps in your history indicate a data issue. Do you have any errors in your logs relating to the integration?
Could I also get about 1 hours worth of debug logs, as this will include the raw responses for this endpoint
I ran debug logging between 1330-1450 today, how can I best share this file with you securely?
There is an error logged in HA, I hadn't noticed this when I typed my reply yesterday.
This error originated from a custom integration.
Logger: custom_components.octopus_energy.coordinators.fan_club_discounts
Source: helpers/update_coordinator.py:380
integration: Octopus Energy (documentation, issues)
First occurred: 22 May 2025 at 00:28:31 (603 occurrences)
Last logged: 13:32:11
Unexpected error fetching A-121F4769_fan_club_discounts data
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 380, in _async_refresh
self.data = await self._async_update_data()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 281, in _async_update_data
return await self.update_method()
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/octopus_energy/coordinators/fan_club_discounts.py", line 89, in async_update_data
hass.data[DOMAIN][account_id][DATA_FAN_CLUB_DISCOUNTS] = await async_refresh_fan_club_discounts(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<6 lines>...
)
^
File "/config/custom_components/octopus_energy/coordinators/fan_club_discounts.py", line 47, in async_refresh_fan_club_discounts
result = await client.async_get_fan_club_discounts(account_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/octopus_energy/api_client/__init__.py", line 919, in async_get_fan_club_discounts
return FanClubResponse.parse_obj(response["data"])
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/pydantic/main.py", line 1222, in parse_obj
return cls.model_validate(obj)
~~~~~~~~~~~~~~~~~~^^^^^
File "/usr/local/lib/python3.13/site-packages/pydantic/main.py", line 627, in model_validate
return cls.__pydantic_validator__.validate_python(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
obj, strict=strict, from_attributes=from_attributes, context=context
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
pydantic_core._pydantic_core.ValidationError: 1 validation error for FanClubResponse
fanClubStatus.0.forecast
Input should be a valid dictionary or instance of ForecastInfo [type=model_type, input_value=None, input_type=NoneType]
For further information visit https://errors.pydantic.dev/2.10/v/model_type
I've released https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/releases/tag/v15.1.0-beta.2 to fix that issue.
I just need the full lines that have the text async_get_fan_club_discounts response which don't have any personal identifiable information as it just contains the discounts.
It would also be good to include the lines which include custom_components.octopus_energy.coordinators.fan_club_discounts and custom_components.octopus_energy.fan_club.. Again, these shouldn't include any personal identifiable information, but if they did it would just be your account id which you should remove
I have extracted async_get_fan_club_discounts response & custom_components.octopus_energy.coordinators.fan_club_discounts from yesterdays logs. There were no entries for custom_components.octopus_energy.fan_club.
I have installed beta 2, if you would like logs from this version let me know.
Nothing is jumping out in those logs other than. Logs from beta 2 would be good please. For custom_components.octopus_energy.fan_club, I'm looking for any row that contains that key, but not exclusively (e.g. custom_components.octopus_energy.fan_club.current_discount)
custom_components.octopus_energy.fan_club is now showing in the logs. I've attached another hours worth of logs from beta 2.
27_5_25 beta 2 Debug.txt
I've released https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/releases/tag/v15.2.0-beta.1 which should hopefully fix this issue
Something funky seems to be going on as Octopus is reporting a lower discount than the integration. See below a crude match up of the previous discount sensor (which I understand is pulled from the historic query and so is the confirmed discount values) vs the display in the Octopus dashboard. Note that during the period for which I have compared the integration with the Octopus dashboard, the maximum discount was 20%.
Logs here 2_6_25.beta.1.Debug.txt
The section of the graph marked in orange is particularly strange to me - what could be prompting this spike from 0% to 20% & 50% whilst the actual discount is 20% consistently.
This could be a result of the frequency the data is being pulled down. The data is being pulled every 30 minutes. This data includes historic, actual and forecast. Like the rate sensors, the previous, current and next discounts combine time periods across the historic, actual and forecast where the discount value is the same (e.g. if there is 20% discount for 10:00 and 10:30 and the current time is 11:15 and at 20% and the forecast for 12:00 is 20% then the current sensor will report 20% from 10:00 to 13:00 (as current time is extended to the end of the hour as we don't have data available for this point). In our example, this means historic will go from the start of the data to 10:00.
If the data is refreshed at 10:45, but the forecast is updated at 10:50, then we'll be working off the old forecast until around 11:15. I might need to look at updating the data more frequently due to the dynamic nature.
It might also be better to map the current discount sensor against what OE said during that same period.
I'll take a look at your log when I get a chance.
I've released https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/releases/tag/v15.4.0-beta.1 which has the latest features from v15.3.0. I've also increased the frequency of dowloading the data.
I raised an issue with Octopus today as no data has been coming through to their app for a while. They confirmed they're currently having issues with turbine comms at Fan Clubs #1 & #2. When there is no data received from the turbine, the API call response is:
2025-07-10 19:02:41.177 DEBUG (MainThread) [custom_components.octopus_energy.api_client] async_get_fan_club_discounts response: {'data': {'fanClubStatus': [{'discountSource': '#1 Fan (GBR): Carr Farm', 'current': {'startAt': '2025-07-10T18:02:41.092897+00:00', 'discount': '0.000'}, 'historic': [], 'forecast': None}]}}
Are any errors raised in this scenario or are the sensors reporting back what is coming back from the API?
Is this feature meeting expectations to be released?
No errors were raised, I would say the integration handled the loss of data well. Everything just went to 0% which is acceptable. The API is now back working.
The current discount sensor is mostly good and now matches the timestamps well. The only issue I see are momentary 'dropouts' on the hour, at some hours. See below. The dropout starts at XX:00:27 and lasts for 1 minute exactly.
For the same time period as the above screenshot, the previous discount sensor is showing 50% most of the time. It has the same 1-minute drop outs where the discount drops to 20%. The only 50% discount during this period was between 20:30-21:00 as correctly shown by the current discount sensor.
I thought this might be due to the integration processing and updating the sensors, however that seems to happen at 15-minute intervals not hourly.
That is odd. I'm not able to recreate this behaviour locally with my mocked data. it would be good to know what the attribute values of the event sensor is at the point it dips to 20% discount. If you're comfortable writing and executing SQL, this can be done via the command provided in the FAQ targeting the event sensor.
If you don't feel comfortable with this, it would be good to create an automation runs when the octopus_energy_fan_club_discounts event is fired before the sensor dips to a discount it shouldn't be registering.