home-assistant / core

:house_with_garden: Open source home automation that puts local control and privacy first.
https://www.home-assistant.io
Apache License 2.0
71.82k stars 30.08k forks source link

OpenUV keeps hammering the API on error #85496

Closed hmmbob closed 1 year ago

hmmbob commented 1 year ago

The problem

I was tweaking my HA setup yesterday, with a lot of restarts of the system and because of that - I ran out of API calls (as the sensors get updated on a restart) - which is fine/understood.

The component doesn't handle this well - it will keep trying to request the data every minute or so until the new API-day starts.

What version of Home Assistant Core has the issue?

core-2023.1.1

What was the last working version of Home Assistant Core?

No response

What type of installation are you running?

Home Assistant Container

Integration causing the issue

OpenUV

Link to integration documentation on our website

https://www.home-assistant.io/integrations/openuv/

Diagnostics information

No response

Example YAML snippet

No response

Anything in the logs that might be useful for us?

And this is just a snippet...... (LatLong redacted but are fine).

2023-01-09 00:01:16.846 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:01:16.853 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:02:37.599 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:02:37.602 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:03:58.347 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:03:58.365 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:05:18.997 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:05:19.001 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:06:39.774 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:06:39.780 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:08:00.351 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:08:00.355 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:09:21.024 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:09:21.027 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:10:41.698 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:10:41.716 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:12:02.390 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:12:02.402 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:13:22.979 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:13:22.982 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:14:43.780 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:14:43.783 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:16:04.360 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:16:04.374 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:17:24.922 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:17:24.924 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:18:45.468 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:18:45.471 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:20:06.195 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:20:06.197 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:21:26.812 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:21:26.821 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:22:47.257 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:22:47.265 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:24:07.935 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:24:07.957 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:25:28.621 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:25:28.623 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:26:49.096 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:26:49.099 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:28:09.612 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:28:09.623 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:29:30.179 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:29:30.182 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:30:50.860 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:30:50.862 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:32:11.474 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:32:11.498 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:33:31.995 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:33:32.001 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:34:52.500 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:34:52.520 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:36:13.226 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:36:13.230 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:37:33.586 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:37:33.601 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:38:54.036 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:38:54.039 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:40:14.405 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:40:14.409 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:41:35.030 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:41:35.060 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:42:55.488 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:42:55.490 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:44:15.848 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:44:15.851 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:45:36.636 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:45:36.656 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:46:57.365 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:46:57.369 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:48:17.789 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:48:17.795 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:49:38.368 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:49:38.369 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:50:59.176 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:50:59.178 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:52:19.682 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:52:19.685 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:53:40.343 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:53:40.346 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:55:00.847 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:55:00.863 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:56:21.406 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:56:21.413 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:57:42.088 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:57:42.091 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:59:02.487 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/uv?lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))
2023-01-09 00:59:02.491 ERROR (MainThread) [pyopenuv.client] Giving up _async_request(...) after 1 tries (aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('https://api.openuv.io/api/v1/protection?from=5.0&to=5.0&lat=51.--redacted--&lng=5.--redacted--&alt=19.0'))

Additional information

No response

home-assistant[bot] commented 1 year ago

Hey there @bachya, mind taking a look at this issue as it has been labeled with an integration (openuv) you are listed as a code owner for? Thanks!

Code owner commands Code owners of `openuv` can trigger bot actions by commenting: - `@home-assistant close` Closes the issue. - `@home-assistant rename Awesome new title` Change the title of the issue. - `@home-assistant reopen` Reopen the issue. - `@home-assistant unassign openuv` Removes the current integration label and assignees on the issue, add the integration domain after the command.

(message by CodeOwnersMention)


openuv documentation openuv source (message by IssueLinks)

bachya commented 1 year ago

Since OpenUV doesn't have automatic API polling, it'd be useful to see the automation you have for updating data.

hmmbob commented 1 year ago

Sure:

    - id: update_openuv
      alias: "Update OpenUV"
      trigger:
        - platform: time_pattern
          minutes: "/25"
        - platform: homeassistant
          event: start
      condition:
        - condition: sun
          after: sunrise
          after_offset: "+00:30:00"
          before: sunset
          before_offset: "-00:30:00"
      action:
        service: homeassistant.update_entity
        target:
          entity_id:
            - binary_sensor.openuv_protection_window
            - sensor.openuv_current_uv_index

But while this is the reason I ran out of API credits (due to restarts and "home assistant start" as trigger), the issue I'm trying to describe comes after you run out of API credits: then the component keeps trying over and over and over again to get the data (until the API finally accepts a request because a new day has started).

The log above is from just 1hr....

bachya commented 1 year ago

I'm confused why that's an issue? Wouldn't you expect the integration to keep trying until it can reestablish a connection?

The API response doesn't give us any clarity on a date or time when the rate limiting will expire, so our only option is to keep trying.

hmmbob commented 1 year ago

True, but every 1min20? Seems a bit much.

Maybe some increasing timeout our so? Or Log the error when receiving a 403 (as that apparently means "out of API credits" for openuv), but don't retry automatically and wait for the next manual update (as this is, as you rightly pointed out, a component without any default polling).

Api credits seem to reset at 00:00 GMT by the looks of my logs

bachya commented 1 year ago

I think I see what's happening.

When OpenUV is first loaded, we disable the retry mechanism used by the underlying library (pyopenuv) and attempt to grab data for the first time—any failure will initiate a retried loading of the entire integration (orchestrated by HASS itself):

https://github.com/home-assistant/core/blob/3bb435c292533033c793b5a72aad1a7749c50d7f/homeassistant/components/openuv/__init__.py#L73-L82

https://github.com/home-assistant/core/blob/3bb435c292533033c793b5a72aad1a7749c50d7f/homeassistant/components/openuv/coordinator.py#L113-L124

As you can see from that second snippet, we watch for two different error conditions:

  1. An indication of an invalid API key (more on this in a moment—it isn't straightforward)
  2. Any other HTTP error

In your case, the first error type is what's occurring.

If this were to happen after the integration loads, we would increment a counter and watch for subsequent HTTP 403s (in short, OpenUV can return a 403 because of an invalid key or an underlying service issue; more background: https://github.com/home-assistant/core/pull/79691). However, in this case, the integration hasn't loaded; Home Assistant itself will retry loading, which unfortunately bypasses our counter (and prevents us from stopping checks).

All this said, it looks like OpenUV has updated their API with some new calls (including one to determine if the service is down). Let me take a look at updating pyopenuv and then return here.

hmmbob commented 1 year ago

Wow, very detailed explanation!

Thanks much anyway for maintaining this integration. While currently it's only rain around here, we use it quite a bit during the nice seasons!

bachya commented 1 year ago

@hmmbob Okay, I have a route forward: https://github.com/home-assistant/core/pull/85573. It's worth quickly reading over the idiosyncrasy:

There is one quirk to keep in mind. In OpenUV, an HTTP 403 response indicates one of two conditions:

  1. An invalid API key
  2. An API key whose daily/monthly limit is reached

Unfortunately, we can't deduce which is which when we receive an HTTP 403 (there is an API endpoint to determine how many calls a user has made per day/month, but there is no way to know what that particular user's call limit is 🤦🏻). So, we follow this strategy:

  1. Any HTTP 403 will trigger a re-auth flow.
  2. In the case of an overrun API call limit, once the limit expires and the coordinator successfully retrieves data, any existing re-auth flow is canceled.

TL;DR: if you have a key whose limit is reached, you'll get a notice about an invalid API key (and an invitation to enter a new one). If you ignore that until the next time your automation runs—after the key limit is reset—that re-auth notice will automatically go away. I'll be opening a docs PR to ensure this is documented.

hmmbob commented 1 year ago

Oh, this is great!

About:

Unfortunately, we can't deduce which is which when we receive an HTTP 403 (there is an API endpoint to determine how many calls a user has made per day/month, but there is no way to know what that particular user's call limit is 🤦🏻).

The API provides you either 50 (free plan) or 15000 (paid) credits a day - is that something to work with?

As in: if i get a 403 AND I have made exactly 50 calls according to the API, then it is safe to assume I ran into my limit of the free plan. Any other value would be inconclusive.

Just a thought. Already very thankful for your great work here!

bachya commented 1 year ago

The API provides you either 50 (free plan) or 15000 (paid) credits a day - is that something to work with?

As in: if i get a 403 AND I have made exactly 50 calls according to the API, then it is safe to assume I ran into my limit of the free plan. Any other value would be inconclusive.

Thanks for the idea, @hmmbob. That could still pose a situation where an API has become invalid and we mistakenly think it's just at its limit. That said, it's an interesting line of though—I'll keep investigating!

hmmbob commented 1 year ago

Came across another piece of code that solves this quite elegantly:

https://github.com/home-assistant/core/blob/dev/homeassistant/components/tomorrowio/__init__.py#L98-L123

Something like this could work. I guess it's safe to assume max 50 daily API calls (I'm not sure why someone would pay $15 for 15000 daily API calls - or IF you want to include this edge case this could be a configuration setting? Linke a checkbox in the settings?), and if we use sunrise/sunset times smartly we can calculate how many minutes should be between updates. Sun info is encompassed in the /api/uv API endpoint, so it's pretty much self contained anyways (no dependency on sun.sun)

hmmbob commented 1 year ago

I was playing with the dashboard of OpenUV, ~and it appears that calls to /api/stat are not counted towards the API credit.~ That's interesting, because. it contains the amount of API calls made already.

IF the API still allows us to call this endpoint when we exceeded the amount of calls, we have a stable way to determine if 'API calls exceeded' is the reason for error.

sample data:

{
    "result": {
        "cost_last_month": 0,
        "cost_month": 0.065,
        "cost_today": 0,
        "cost_yesterday": 0,
        "requests_last_month": 1398,
        "requests_month": 310,
        "requests_today": 46,
        "requests_yesterday": 34
    }
}

Edit: playing around a bit more revealed two things:

edit2:

bob@debian:~/docker/smarthome/homeassistant$ curl -X GET 'https://api.openuv.io/api/v1/uv?lat=51.0&lng=5.0&alt=100&dt=' -H 'x-access-token: 1234567890'
{"error":"User with API Key not found"}

So, by looking to the respons JSON we can determine what is the issue:

{ "error": "Daily API quota exceeded. Add billing details to get 15000 reqs/day or contact support@openuv.io to upgrade to Unlimited Plan."}

or

{"error":"User with API Key not found"}

Tagging you because I don't know if you still receive notifications for this thread; @bachya

bachya commented 1 year ago

Great detective work, @hmmbob! I'll definitely give this a look.