mypyllant-component icon indicating copy to clipboard operation
mypyllant-component copied to clipboard

401 error after ~24h passed on time-windows API

Open zaubara opened this issue 9 months ago • 6 comments
trafficstars

Before submitting a new issue

Problem description

I have an automation running daily that sets the time windows for tomorrow (dynamic electricity pricing). This works fine, however on the second day I now always get a 401 error. This does seem to be completely unrelated to the api rate limit - it’s a different error, and I have set the requests to only update every 10800 seconds (and 3600 - same problem). I would assume that an old token is being used, maybe one that needs to be refreshed. When I restart home assistant or reload the integration, running the same automation runs successfully.

Edit: I just noticed that waiting only a few minutes is enough - I waited 10 minutes and called it - 401. Restarted the integration - success.

Logs

logger:%20homeassistant.components.automation.vaillant_warmepumpe_gunstigste_stunden_heizen%0AQuelle%3A%20components/automation/__init__.py:718%0AIntegration:%20Automation%20(Dokumentation,%20Probleme)%0AErstmals%20aufgetreten:%2015:01:00%20(1%20Vorkommnisse)%0AZuletzt%20protokolliert:%2015:01:00%0A%0AWhile%20executing%20automation%20automation.vaillant_warmepumpe_gunstigste_stunden_heizen%0ATraceback%20(most%20recent%20call%20last):%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/components/automation/__init__.py%22,%20line%20718,%20in%20async_trigger%0A%20%20%20%20return%20await%20self.action_script.async_run(%0A%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20%20%20%20%20%20%20variables,%20trigger_context,%20started_action%0A%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20%20%20)%0A%20%20%20%20%5E%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/script.py%22,%20line%201810,%20in%20async_run%0A%20%20%20%20return%20await%20asyncio.shield(create_eager_task(run.async_run()))%0A%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/script.py%22,%20line%20464,%20in%20async_run%0A%20%20%20%20await%20self._async_step(log_exceptions=False)%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/script.py%22,%20line%20528,%20in%20_async_step%0A%20%20%20%20self._handle_exception(%0A%20%20%20%20~~~~~~~~~~~~~~~~~~~~~~%5E%0A%20%20%20%20%20%20%20%20ex,%20continue_on_error,%20self._log_exceptions%20or%20log_exceptions%0A%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20%20%20)%0A%20%20%20%20%5E%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/script.py%22,%20line%20558,%20in%20_handle_exception%0A%20%20%20%20raise%20exception%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/script.py%22,%20line%20526,%20in%20_async_step%0A%20%20%20%20await%20getattr(self,%20handler)()%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/script.py%22,%20line%20764,%20in%20_async_call_service_step%0A%20%20%20%20response_data%20=%20await%20self._async_run_long_action(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20%20%20...%3C9%20lines%3E...%0A%20%20%20%20)%0A%20%20%20%20%5E%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/script.py%22,%20line%20727,%20in%20_async_run_long_action%0A%20%20%20%20return%20await%20long_task%0A%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/core.py%22,%20line%202795,%20in%20async_call%0A%20%20%20%20response_data%20=%20await%20coro%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/core.py%22,%20line%202838,%20in%20_execute_service%0A%20%20%20%20return%20await%20target(service_call)%0A%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/service.py%22,%20line%201006,%20in%20entity_service_call%0A%20%20%20%20single_response%20=%20await%20_handle_entity_call(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20%20%20%20%20%20%20hass,%20entity,%20func,%20data,%20call.context%0A%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20%20%20)%0A%20%20%20%20%5E%0A%20%20File%20%22/usr/src/homeassistant/homeassistant/helpers/service.py%22,%20line%201078,%20in%20_handle_entity_call%0A%20%20%20%20result%20=%20await%20task%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20File%20%22/config/custom_components/mypyllant/climate.py%22,%20line%20567,%20in%20set_time_program%0A%20%20%20%20await%20self.coordinator.api.set_zone_time_program(%0A%20%20%20%20%20%20%20%20self.zone,%20program_type,%20time_program%0A%20%20%20%20)%0A%20%20File%20%22/usr/local/lib/python3.13/site-packages/myPyllant/api.py%22,%20line%20804,%20in%20set_zone_time_program%0A%20%20%20%20await%20self.aiohttp_session.patch(%0A%20%20%20%20...%3C3%20lines%3E...%0A%20%20%20%20)%0A%20%20File%20%22/usr/local/lib/python3.13/site-packages/myPyllant/http_client.py%22,%20line%2030,%20in%20_request%0A%20%20%20%20return%20await%20super()._request(method,%20url,%20**kwargs)%0A%20%20%20%20%20%20%20%20%20%20%20%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%0A%20%20File%20%22/usr/local/lib/python3.13/site-packages/aiohttp/client.py%22,%20line%20832,%20in%20_request%0A%20%20%20%20await%20raise_for_status(resp)%0A%20%20File%20%22/usr/local/lib/python3.13/site-packages/myPyllant/http_client.py%22,%20line%2083,%20in%20on_raise_for_status%0A%20%20%20%20response.raise_for_status()%0A%20%20%20%20~~~~~~~~~~~~~~~~~~~~~~~~~%5E%5E%0A%20%20File%20%22/usr/local/lib/python3.13/site-packages/aiohttp/client_reqrep.py%22,%20line%201161,%20in%20raise_for_status%0A%20%20%20%20raise%20ClientResponseError(%0A%20%20%20%20...%3C5%20lines%3E...%0A%20%20%20%20)%0Aaiohttp.client_exceptions.ClientResponseError:%20401,%20message='Unauthorized',%20url='https://api.vaillant-group.com/service-connected-control/end-user-app-api/v1/systems/f24ea462-6faa-4ef6-8204-c051e47ab881/tli/zones/0/time-windows'%0A

zaubara avatar Jan 24 '25 15:01 zaubara

Same issue here. I tried to change polling intervals but it seems that those settings have no effect:

Image

guglez avatar Feb 02 '25 10:02 guglez

After I increased the interval to a quite ridiculous amount to avoid 401 errors (the API limit one) - that's 10800 seconds - the request count finally did drop: Image

However, I was thinking that my large interval somehow leads to the 401 error on all other requests. I noticed that I also cannot, for instance, change the water heater from time controlled to cylinder boost, so it's not isolated to the mentioned API. I was hoping that, with a reasonable interval, the token gets refreshed correctly - but I think that's not connected.

Here's my guess, without having a look at the API: Vaillant uses OAuth and we have a (long lived) refresh token and a (short lived) access token. Some update probably broke the token refresh process after the access token is expired. Case in point: I added an automation that refreshes the myVAILLANT plugin, before my other automations run - and that makes the rest of the automations work. Here's the script, if anyone needs it until this gets fixed - just edit the trigger and the entry_id to your needs:

alias: Refresh myPyllant
description: ""
triggers:
  - trigger: time
    at: "15:00:00"
conditions: []
actions:
  - action: homeassistant.reload_config_entry
    data:
      entry_id: 123412341234123412341234
mode: single
 

zaubara avatar Feb 02 '25 11:02 zaubara

@zaubara request count dropped because you restarted the integration and not because the rate lowered.

guglez avatar Feb 02 '25 11:02 guglez

No, the requests per hour dropped overall. Yes, they reset to 0 after the restart - but that's not what I'm talking about. Here's a longer view:

Image

zaubara avatar Feb 02 '25 11:02 zaubara

To avoid exceeding quota limits, I set the seconds between updates to 840 seconds. This results in an update every 14 minutes.

However, when I change the temperature or operation mode on the climate control and the last update was more than 4 minutes ago, I receive a 401 error:

aiohttp.client_exceptions.ClientResponseError: 401, message='Unauthorized', url='https://api.vaillant-group.com/service-connected-control/end-user-app-api/v1/systems/xxxxxxx/tli/zones/0/heating-operation-mode'.

It seems that the token is timing out after about 4 minutes.

To address this issue, I added an ensure_token_refresh decorator to the climate.py file and applied it to the functions with API calls. This solution is working with my installation.

Since I am not deeply involved in this project, I kindly ask @signalkraft to verify if this fix can be permanently incorporated into the project.

Thank you all for this great project, and special thanks to @signalkraft for the excellent work!

janlenck avatar Feb 07 '25 11:02 janlenck

Here's the pre-release with the corresponding PR merged: https://github.com/signalkraft/mypyllant-component/releases/tag/v0.9.2b0

signalkraft avatar Feb 20 '25 18:02 signalkraft

@zaubara was the issue fixed?

boelle avatar May 13 '25 08:05 boelle

@signalkraft think we can close this one as @zaubara does not have an issue anymore

boelle avatar Oct 26 '25 08:10 boelle

Sorry - yes, the issue has been fixed. Thanks to everyone involved 👍

zaubara avatar Oct 26 '25 11:10 zaubara