Skip to content

Improve Tibber price coordinator#166175

Merged
MartinHjelmare merged 15 commits into
devfrom
166107
Apr 10, 2026
Merged

Improve Tibber price coordinator#166175
MartinHjelmare merged 15 commits into
devfrom
166107

Conversation

@Danielhiversen
Copy link
Copy Markdown
Member

@Danielhiversen Danielhiversen commented Mar 22, 2026

Breaking change

Proposed change

Raising UpdateFailed makes the data we already have unavailable for the sensor

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies a diff between library versions and ideally a link to the changelog/release notes is added to the PR description.

To help with the load of incoming pull requests:

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
Comment thread homeassistant/components/tibber/coordinator.py Outdated
@MartinHjelmare MartinHjelmare marked this pull request as draft March 22, 2026 20:10
@IvovanWilligen
Copy link
Copy Markdown

IvovanWilligen commented Mar 23, 2026

Added Tibber as a custum component. Uploaded the new coordinator.py, restarted HA but still getting errors (new to old).

Logger: tibber.realtime
Source: runner.py:289
First occurred: 11:02:32 PM (2 occurrences)
Last logged: 11:07:33 PM

Watchdog: Connection is down, 2026-03-23 22:02:42.032745+00:00
Watchdog: Connection is down, 2026-03-23 22:07:43.313358+00:00
Logger: custom_components.tibber.coordinator
Source: custom_components/tibber/coordinator.py:319
integration: Tibber
First occurred: 11:00:21 PM (1 occurrence)
Last logged: 11:00:21 PM

Error communicating with Tibber API (504): Unexpected content type: text/html
Logger: tibber
Source: runner.py:289
First occurred: 11:00:21 PM (1 occurrence)
Last logged: 11:00:21 PM

Temporary failure interacting with Tibber API, HTTP status: 504. API error: UNKNOWN / Unexpected content type: text/html
Logger: tibber.data_api
Source: custom_components/tibber/coordinator.py:384
integration: Tibber
First occurred: 10:54:28 PM (2 occurrences)
Last logged: 10:54:40 PM

Error getting device Too many requests. Your IP is now temporarily banned for calling API for a few minutes.
Logger: tibber.data_api
Source: runner.py:289
First occurred: 10:54:11 PM (50 occurrences)
Last logged: 10:54:40 PM

Rate limited (429), waiting 6.37 seconds before retry (attempt 1/2)
Rate limited (429), waiting 8.93 seconds before retry (attempt 1/2)
Rate limited (429), waiting 10.00 seconds before retry (attempt 2/2)
Rate limit exceeded: max attempts (2) reached
Temporary failure interacting with Tibber Data API, HTTP status: 429. API error: HTTP_429 / Too many requests. Your IP is now temporarily banned for calling API for a few minutes.
Logger: custom_components.tibber.coordinator
Source: helpers/update_coordinator.py:497
integration: Tibber
First occurred: 10:54:28 PM (1 occurrence)
Last logged: 10:54:28 PM

Error fetching tibber Data API data: Rate limit exceeded, retry after 11.541949580472657 seconds

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
@IvovanWilligen

This comment was marked as outdated.

@IvovanWilligen

This comment was marked as outdated.

@MartinHjelmare
Copy link
Copy Markdown
Member

This PR isn't finished yet. Testing at this stage isn't really useful. Please let the PR author ask for testers before testing.

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
Copilot AI review requested due to automatic review settings April 3, 2026 09:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the Tibber integration’s price/data refresh behavior to avoid losing existing sensor values when the upstream API intermittently fails (e.g., HTTP 504), addressing #166107.

Changes:

  • Update the price coordinator to refresh price info only when local-day prices appear missing, and compute the next refresh interval as a timedelta.
  • Trigger async_config_entry_first_refresh() for the price and data coordinators during sensor platform setup.
  • In the get_prices service, proactively refresh a home’s price info when cached prices don’t appear valid.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
homeassistant/components/tibber/services.py Adds validity checks and conditional refresh for get_prices service responses.
homeassistant/components/tibber/sensor.py Ensures coordinators perform their first refresh during setup before entities rely on them.
homeassistant/components/tibber/coordinator.py Refactors the price coordinator’s refresh criteria and update interval calculation.

Comment thread homeassistant/components/tibber/services.py Outdated
Comment thread homeassistant/components/tibber/coordinator.py Outdated
Comment thread homeassistant/components/tibber/sensor.py Outdated
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
@Danielhiversen Danielhiversen marked this pull request as ready for review April 5, 2026 12:55
Copilot AI review requested due to automatic review settings April 5, 2026 12:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread homeassistant/components/tibber/services.py Outdated
Comment thread homeassistant/components/tibber/services.py Outdated
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 9, 2026 03:41
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

homeassistant/components/tibber/coordinator.py:338

  • Add a regression test that simulates a temporary Tibber API failure after prices have already been fetched, and assert the price coordinator keeps the last successful data (and the price sensor stays available) when no refresh is needed.
        try:
            if homes_to_update:
                await asyncio.gather(
                    *(home.update_info_and_price_info() for home in homes_to_update)
                )

@Danielhiversen Danielhiversen marked this pull request as ready for review April 9, 2026 19:50
Copilot AI review requested due to automatic review settings April 9, 2026 19:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment on lines 335 to 337
if homes_to_update:
await asyncio.gather(
*(home.update_info_and_price_info() for home in homes_to_update)
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prevent the electricity price sensor from becoming unavailable on transient failures by ensuring errors from update_info_and_price_info() don’t cause the coordinator update to fail when existing price data is still usable (e.g., keep returning cached data and only fail if no valid prices are present).

Copilot uses AI. Check for mistakes.
@steynovich
Copy link
Copy Markdown

With the changes on this PR so far I do get the following error:

  homeassistant-1  | 2026-04-10 08:53:18.707 ERROR (MainThread) [homeassistant] Error doing job: Task exception was never retrieved (task: None)
  homeassistant-1  | Traceback (most recent call last):
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/components/template/coordinator.py", line 140, in _handle_triggered_with_script
  homeassistant-1  |     if script_result := await self._script.async_run(run_variables, script_context):
  homeassistant-1  |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 1865, in async_run
  homeassistant-1  |     return await asyncio.shield(create_eager_task(run.async_run()))
  homeassistant-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 467, in async_run
  homeassistant-1  |     await self._async_step(log_exceptions=False)
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 533, in _async_step
  homeassistant-1  |     self._handle_exception(
  homeassistant-1  |     ~~~~~~~~~~~~~~~~~~~~~~^
  homeassistant-1  |         ex, continue_on_error, self._log_exceptions or log_exceptions
  homeassistant-1  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  homeassistant-1  |     )
  homeassistant-1  |     ^
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 563, in _handle_exception
  homeassistant-1  |     raise exception
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 531, in _async_step
  homeassistant-1  |     await getattr(self, handler)()
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 1018, in _async_step_call_service
  homeassistant-1  |     response_data = await self._async_run_long_action(
  homeassistant-1  |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  homeassistant-1  |     ...<9 lines>...
  homeassistant-1  |     )
  homeassistant-1  |     ^
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 631, in _async_run_long_action
  homeassistant-1  |     return await long_task
  homeassistant-1  |            ^^^^^^^^^^^^^^^
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/core.py", line 2817, in async_call
  homeassistant-1  |     response_data = await coro
  homeassistant-1  |                     ^^^^^^^^^^
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/core.py", line 2860, in _execute_service
  homeassistant-1  |     return await target(service_call)
  homeassistant-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/components/tibber/services.py", line 47, in __get_prices
  homeassistant-1  |     tibber_connection = await entries[0].runtime_data.async_get_client(call.hass)
  homeassistant-1  |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  homeassistant-1  |   File "/usr/src/homeassistant/homeassistant/components/tibber/__init__.py", line 64, in async_get_client
  homeassistant-1  |     await self._client.set_access_token(access_token)
  homeassistant-1  | TypeError: 'NoneType' object can't be awaited

I guess manifest.json needs do be updated to require pyTibber==0.37.0?

@Danielhiversen
Copy link
Copy Markdown
Member Author

I think this is something wrong with your local setup;

"requirements": ["pyTibber==0.37.0"]

@steynovich
Copy link
Copy Markdown

I think this is something wrong with your local setup;

"requirements": ["pyTibber==0.37.0"]

yes, sorry. It's fixed now.

Copy link
Copy Markdown
Member

@MartinHjelmare MartinHjelmare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this now. It may not solve the problem completely but I think it's an improvement.

@MartinHjelmare MartinHjelmare changed the title Handle failed update in Tibber Improve Tibber price coordinator Apr 10, 2026
@MartinHjelmare MartinHjelmare merged commit 35ffffb into dev Apr 10, 2026
37 checks passed
@MartinHjelmare MartinHjelmare deleted the 166107 branch April 10, 2026 07:57
@MartinHjelmare MartinHjelmare added this to the 2026.4.2 milestone Apr 10, 2026
@dns13
Copy link
Copy Markdown

dns13 commented Apr 10, 2026

I've upgraded from some of the first commits of this MR (unfortunately did not remember which one exactly) to the now merged one and at least the availability got worse, again.

Before the update the prices were pretty stable and only the occasional hiccups which required reloading the integration were left.

Bildschirmfoto 2026-04-10 um 19 14 17

@MartinHjelmare
Copy link
Copy Markdown
Member

I don't think you've tested the final version of this PR. There should at most be a single gap in the plot per afternoon with this version, unless there's something we've missed. As noted above we haven't solved the issue completely with this PR, but it should be an improvement.

If you want to provide feedback, please test the merged version of the PR. Anything else is unfortunately not useful.

@dns13
Copy link
Copy Markdown

dns13 commented Apr 10, 2026

I checked out the dev branch which should include the merge, but I'll double check and head back.

@frenck frenck removed this from the 2026.4.2 milestone Apr 10, 2026
@MartinHjelmare MartinHjelmare mentioned this pull request Apr 10, 2026
@dns13
Copy link
Copy Markdown

dns13 commented Apr 10, 2026

Edit: after updating to 2026.4.2 and removing the override the price keeps being available even if the request fails. The integration is still getting stuck on the full hour more often than with the version in 2026.4.1 but that's not in scope of this MR. (It's discussed here: #168007) Thank you for your work!

I made sure to have the merged version and double checked, that the custom override is actually loaded. Occasionally there is no price data for the first 15 minutes of the hour. It happens when the Tibber API is returning odd data. Price data is also unavailable after a HA restart until the next 15 minute slot.

Both things were not happening in the commit I've used before, even when the API got crazy.

Edit: If it gets unavailable on the full hour the complete Integration is hanging and requires my watchdog automation to reload it. I've still had this reloads before but not nearly that often and not coupled to the full hour.

Logger: custom_components.tibber.coordinator
Quelle: helpers/update_coordinator.py:426
Integration: Tibber
Erstmals aufgetreten: 10. April 2026 um 23:00:19 (1 Vorkommnis)
Zuletzt protokolliert: 10. April 2026 um 23:00:19
Unexpected error fetching tibber price data

Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 426, in _async_refresh
self.data = await self._async_update_data()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/tibber/coordinator.py", line 293, in _async_update_data
tibber_connection = await self.config_entry.runtime_data.async_get_client(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.hass
^^^^^^^^^
)
^
File "/config/custom_components/tibber/init.py", line 64, in async_get_client
await self._client.set_access_token(access_token)
File "/usr/local/lib/python3.14/site-packages/tibber/init.py", line 238, in set_access_token
await self.update_info()
File "/usr/local/lib/python3.14/site-packages/tibber/init.py", line 140, in update_info
if (data := await self.execute(INFO)) is None:
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/site-packages/tibber/init.py", line 123, in execute
return (await extract_response_data(resp)).get("data")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/site-packages/tibber/response_handler.py", line 37, in extract_response_data
raise RetryableHttpExceptionError(
...<3 lines>...
)
tibber.exceptions.RetryableHttpExceptionError: Unexpected content type: text/html

@frenck frenck added this to the 2026.4.2 milestone Apr 11, 2026
frenck pushed a commit that referenced this pull request Apr 11, 2026
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Electricity price unavailable after 2026.3.3 upgrade

9 participants