Somehow I had to delete the token.json to fix it and it was recreated with the same permissions.
I can't find why it was not able to deal with the token refresh.
2026-04-13 23:22:24,292 [INFO] [__main__:146] WebSocket restart delayed by 4.0s
2026-04-13 23:22:28,290 [WARNING] [__main__:149] Websocket is DEAD, restarting
2026-04-13 23:22:28,291 [INFO] [__main__:166] API restart delayed by 0.0s
2026-04-13 23:22:28,291 [INFO] [somfy_protect.websocket:107] Websocket token expired, refreshing
2026-04-13 23:22:28,291 [INFO] [somfy_protect.sso:280] Refreshing Token
2026-04-13 23:22:28,292 [WARNING] [__main__:169] API is DEAD, restarting
2026-04-13 23:22:28,292 [INFO] [root:39] Init SomfyProtect2Mqtt
Exception in thread 2026-04-13 23:22:28,330 [INFO] [somfy_protect.sso:280] Refreshing Token
somfy-websocket-loop:
Traceback (most recent call last):
File "/usr/local/lib/python3.12/threading.py", line 1075, in _bootstrap_inner
self.run()
File "/usr/local/lib/python3.12/threading.py", line 1012, in run
self._target(*self._args, **self._kwargs)
File "/app/main.py", line 61, in somfy_protect_wss_loop
websocket_client = SomfyProtectWebsocket(sso=sso, debug=debug, config=config, mqtt_client=mqtt_client, api=api)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/websocket/__init__.py", line 86, in __init__
self.token = self._load_token()
^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/websocket/__init__.py", line 108, in _load_token
token = self.sso.refresh_tokens()
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/sso/__init__.py", line 325, in refresh_tokens
return self._refresh_tokens_locked()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/sso/__init__.py", line 286, in _refresh_tokens_locked
token = self._oauth.refresh_token(SOMFY_PROTECT_TOKEN)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/requests_oauthlib/oauth2_session.py", line 496, in refresh_token
self.token = self._client.parse_request_body_response(r.text, scope=self.scope)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 426, in parse_request_body_response
self.token = parse_token_response(body, scope=scope)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 444, in parse_token_response
validate_token_parameters(params)
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 451, in validate_token_parameters
raise_from_error(params.get('error'), params)
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/errors.py", line 398, in raise_from_error
raise cls(**kwargs)
oauthlib.oauth2.rfc6749.errors.InvalidGrantError: (invalid_grant)
Exception in thread somfy-api-loop:
Traceback (most recent call last):
File "/app/somfy_protect/sso/__init__.py", line 251, in _request_locked
return getattr(self._oauth, method)(url, **kwargs), False
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/requests/sessions.py", line 602, in get
return self.request("GET", url, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/requests_oauthlib/oauth2_session.py", line 528, in request
url, headers, data = self._client.add_token(
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 216, in add_token
raise TokenExpiredError()
oauthlib.oauth2.rfc6749.errors.TokenExpiredError: (token_expired)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.12/threading.py", line 1075, in _bootstrap_inner
self.run()
File "/usr/local/lib/python3.12/threading.py", line 1012, in run
self._target(*self._args, **self._kwargs)
File "/app/main.py", line 35, in somfy_protect_loop
somfy_protect_api = SomfyProtect2Mqtt(api=api, mqtt_client=mqtt_client, config=config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect_2_mqtt.py", line 64, in __init__
sites = self.api.get_sites()
^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/api/__init__.py", line 179, in get_sites
response = self.get("/v3/site")
^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/api/__init__.py", line 147, in get
return self._request("get", path, base_url)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/api/__init__.py", line 107, in _request
response, refreshed = self.sso.request(method, url, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/sso/__init__.py", line 240, in request
response, expired_refresh = self._request_locked(method, url, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/somfy_protect/sso/__init__.py", line 253, in _request_locked
self._refresh_tokens_locked()
File "/app/somfy_protect/sso/__init__.py", line 286, in _refresh_tokens_locked
token = self._oauth.refresh_token(SOMFY_PROTECT_TOKEN)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/requests_oauthlib/oauth2_session.py", line 496, in refresh_token
self.token = self._client.parse_request_body_response(r.text, scope=self.scope)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 426, in parse_request_body_response
self.token = parse_token_response(body, scope=scope)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 444, in parse_token_response
validate_token_parameters(params)
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 451, in validate_token_parameters
raise_from_error(params.get('error'), params)
File "/usr/local/lib/python3.12/site-packages/oauthlib/oauth2/rfc6749/errors.py", line 398, in raise_from_error
raise cls(**kwargs)
oauthlib.oauth2.rfc6749.errors.InvalidGrantError: (invalid_grant)
Using the latest 2026.3.1
The container created the token.json by itlsef with the following permissions
-rw------- 1 5678 5678 token.jsonThen recently it had to refresh the token but fall into a boot/refresh loop.
Somehow I had to delete the token.json to fix it and it was recreated with the same permissions.
I can't find why it was not able to deal with the token refresh.