Skip to content

Commit

Permalink
Added logic in coordinator update to check if sync websocket is still…
Browse files Browse the repository at this point in the history
… alive. If not, we will try a token refresh/login. This ensures connectivity is resumed after an outage
  • Loading branch information
gndean committed Jun 22, 2024
1 parent 6ee642b commit 26a635b
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 34 deletions.
77 changes: 44 additions & 33 deletions custom_components/hypervolt_charger/hypervolt_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ async def unload(self):

if self.websocket_sync:
await self.websocket_sync.close()
self.websocket_sync = None

if self.websocket_session_in_progress:
await self.websocket_session_in_progress.close()
self.websocket_session_in_progress = None

async def login(self, session: aiohttp.ClientSession) -> str:
"""If we have an access token, attempt to refresh it.
Expand All @@ -83,6 +86,7 @@ async def login(self, session: aiohttp.ClientSession) -> str:
pass

try:
_LOGGER.info("Attempting log in")
session.headers["user-agent"] = self.get_user_agent()

async with session.post(
Expand Down Expand Up @@ -142,42 +146,49 @@ async def refresh_access_token(self, session: aiohttp.ClientSession):
Store new self.access_token and self.refresh_token.
Also sets the access token in the session and returns it."""

_LOGGER.info("Refreshing access token")
session.headers["user-agent"] = self.get_user_agent()
async with session.post(
"https://kc.prod.hypervolt.co.uk/realms/retail-customers/protocol/openid-connect/token",
data={
"client_id": "home-assistant",
"grant_type": "refresh_token",
"refresh_token": self.refresh_token
}
) as response:
if response.status >= 200 and response.status < 300:
_LOGGER.info("Access token refreshed")
try:
_LOGGER.info("Attempting to refresh access token")
session.headers["user-agent"] = self.get_user_agent()
async with session.post(
"https://kc.prod.hypervolt.co.uk/realms/retail-customers/protocol/openid-connect/token",
data={
"client_id": "home-assistant",
"grant_type": "refresh_token",
"refresh_token": self.refresh_token
}
) as response:
if response.status >= 200 and response.status < 300:
_LOGGER.info("Access token refreshed")

response_text = await response.text()
response_text = await response.text()

self.refresh_token = json.loads(response_text)["refresh_token"]
self.access_token = json.loads(response_text)["access_token"]
session.headers["authorization"] = f"Bearer {self.access_token}"
self.refresh_token = json.loads(response_text)["refresh_token"]
self.access_token = json.loads(response_text)["access_token"]
session.headers["authorization"] = f"Bearer {self.access_token}"

return self.access_token
return self.access_token

elif response.status >= 400 and response.status < 500:
_LOGGER.error(
f"Authentication error when trying to refresh token, status code: {response.status}"
)
# Don't attempt to use the tokens again
self.access_token = None
self.refresh_token = None
elif response.status >= 400 and response.status < 500:
_LOGGER.warning(
f"Authentication error when trying to refresh token, status code: {response.status}"
)
# Don't attempt to use the tokens again
self.access_token = None
self.refresh_token = None

raise InvalidAuth
else:
response_text = await response.text()
_LOGGER.error(
f"Error: unable to refresh token, status: {response.status}, {response_text}",
)
raise CannotConnect
raise InvalidAuth
else:
response_text = await response.text()
_LOGGER.warning(
f"Error: unable to refresh token, status: {response.status}, {response_text}",
)
raise CannotConnect
except Exception as exc:
# Don't log as error or warning as this could just be a network issue
_LOGGER.info(
f"Unable to refresh token: {exc}",
)
raise

async def get_chargers(self, session):
"""Returns an array like: [{"charger_id": 123, "created": "yyyy-MM-ddTHH:mm:ss.sssZ"}]
Expand Down Expand Up @@ -341,7 +352,7 @@ async def on_sync_websocket_message(
message,
)
except Exception as exc:
_LOGGER.warning(f"On_sync_websocket_message_callback error ${exc}")
_LOGGER.warning(f"On_sync_websocket_message_callback error {exc}")

async def on_sync_websocket_connected(self, websocket, access_token):
self.websocket_sync = websocket
Expand Down Expand Up @@ -442,7 +453,7 @@ async def notify_on_websocket(
task_cancelled = True
raise exc
except Exception as exc:
_LOGGER.warning(f"{log_prefix} exception ${exc}")
_LOGGER.warning(f"{log_prefix} exception {exc}")
continue

finally:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ async def _update_with_fallback(self, retry=True) -> HypervoltDeviceState:
self.api_session and
not self.api_session.closed
and "authorization" in self.api_session.headers
and self.api.websocket_sync
):
# If we're a v3 charger, we don't need to do anything, as everything is synced
# via the sync websocket
Expand Down
2 changes: 1 addition & 1 deletion custom_components/hypervolt_charger/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
"requirements": [
"websockets==11.0.3"
],
"version": "2.2.5"
"version": "2.2.6"
}

0 comments on commit 26a635b

Please sign in to comment.