HomeAssistant-OctopusEnergy icon indicating copy to clipboard operation
HomeAssistant-OctopusEnergy copied to clipboard

Octopus Fan Club support

Open BottlecapDave opened this issue 2 years ago • 32 comments

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?)

Screenshot 2023-11-17 at 21 28 23

https://www.octopusenergygeneration.com/fan-club/

See original discussion for plan of action, along with some API examples.

BottlecapDave avatar Nov 20 '23 06:11 BottlecapDave

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.

HITESHLAD1985 avatar Sep 06 '24 05:09 HITESHLAD1985

Fellow Fan #1 member here! I would love to see this implemented within the integration.

BenGlossop09 avatar Oct 04 '24 17:10 BenGlossop09

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.

  1. 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.
  1. 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_discount with 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.
  1. 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_discount field)
  • This should be unit tested.

BottlecapDave avatar Dec 30 '24 10:12 BottlecapDave

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.

BenGlossop09 avatar Dec 31 '24 18:12 BenGlossop09

this would be very useful

deemuuu avatar Jan 24 '25 20:01 deemuuu

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;

  1. 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.
  2. 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.
  3. 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): Image

Sensor 3 (actual discount applied, note the graphed data does not take into account the discount timestamp): Image

Sensor 3 (data processed with Apex Charts to show the correct discount at the correct timestamp, as seen in the Octopus Energy Dashboard) Image Image

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.

BenGlossop09 avatar Feb 27 '25 18:02 BenGlossop09

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 :)

BottlecapDave avatar Feb 28 '25 07:02 BottlecapDave

@BenGlossop09 Did you get any further with bringing this into this integration?

BottlecapDave avatar May 10 '25 16:05 BottlecapDave

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.

BenGlossop09 avatar May 11 '25 14:05 BenGlossop09

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.

BottlecapDave avatar May 11 '25 15:05 BottlecapDave

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."

BenGlossop09 avatar May 12 '25 12:05 BenGlossop09

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.

BottlecapDave avatar May 19 '25 06:05 BottlecapDave

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. Screenshot_20250521-174914.png

Screenshot_20250521-174908.png

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. Screenshot_20250522-143459.png

Screenshot_20250522-143610.png

BenGlossop09 avatar May 22 '25 13:05 BenGlossop09

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?

BottlecapDave avatar May 22 '25 18:05 BottlecapDave

Could I also get about 1 hours worth of debug logs, as this will include the raw responses for this endpoint

BottlecapDave avatar May 23 '25 05:05 BottlecapDave

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

BenGlossop09 avatar May 23 '25 14:05 BenGlossop09

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.

BottlecapDave avatar May 23 '25 21:05 BottlecapDave

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

BottlecapDave avatar May 24 '25 09:05 BottlecapDave

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.

2025-05-23 133011.108 ERROR (MainTh.txt

BenGlossop09 avatar May 24 '25 09:05 BenGlossop09

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)

BottlecapDave avatar May 24 '25 16:05 BottlecapDave

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

BenGlossop09 avatar May 27 '25 10:05 BenGlossop09

I've released https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/releases/tag/v15.2.0-beta.1 which should hopefully fix this issue

BottlecapDave avatar May 31 '25 09:05 BottlecapDave

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%. Image

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.

BenGlossop09 avatar Jun 02 '25 21:06 BenGlossop09

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.

BottlecapDave avatar Jun 05 '25 05:06 BottlecapDave

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.

BottlecapDave avatar Jun 28 '25 08:06 BottlecapDave

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}]}}

BenGlossop09 avatar Jul 10 '25 18:07 BenGlossop09

Are any errors raised in this scenario or are the sensors reporting back what is coming back from the API?

BottlecapDave avatar Jul 12 '25 08:07 BottlecapDave

Is this feature meeting expectations to be released?

BottlecapDave avatar Aug 02 '25 14:08 BottlecapDave

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.

Image

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.

Image

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.

BenGlossop09 avatar Aug 02 '25 17:08 BenGlossop09

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.

BottlecapDave avatar Aug 04 '25 19:08 BottlecapDave