Skip to content

Commit

Permalink
fixing 401 issue?
Browse files Browse the repository at this point in the history
  • Loading branch information
marq24 committed Dec 3, 2024
1 parent 4fd88f5 commit 493e657
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 69 deletions.
171 changes: 103 additions & 68 deletions custom_components/tibber_graphapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def __init__(self, hass: HomeAssistant, config_entry):

# support for systems where vehicle index is not 0
self.bridge = TibberGraphApiBridge(user=self._user, pwd=the_pwd,
websession=async_get_clientsession(hass),
a_web_session=async_get_clientsession(hass),
veh_index=self._vehicle_index,
veh_id=self._vehicle_id)

Expand Down Expand Up @@ -136,16 +136,29 @@ async def init_on_load(self):
"name": f"Tibber GraphAPI {self._vehicle_name}"
}

async def async_refresh_with_pause(self):
await asyncio.sleep(5)
await self.async_refresh()

async def _async_update_data(self):
_LOGGER.debug(f"_async_update_data called")
try:
result = await self.bridge.update()
if result is not None:
_LOGGER.debug(f"number of fields after query: {len(result)}")
else:
if self.bridge.should_be_refreshed():
_LOGGER.debug(f"we going to call async_refresh_with_pause() again")
try:
asyncio.create_task(self.async_refresh_with_pause())
except Exception as exc:
_LOGGER.debug(f"Exception while try to call 'asyncio.create_task(self.async_refresh_with_pause())': {exc}")

return result

except UpdateFailed as exception:
raise UpdateFailed() from exception

except Exception as other:
_LOGGER.warning(f"unexpected: {other}")
raise UpdateFailed() from other
Expand Down Expand Up @@ -226,7 +239,6 @@ def should_poll(self) -> bool:
return False



class TibberGraphApiBridge:
# https://app.tibber.com/v4/gql

Expand All @@ -241,32 +253,36 @@ class TibberGraphApiBridge:
"Content-Type": "application/x-www-form-urlencoded"
}

a_refresh_token = None
tibber_vehicleId = None
tibber_vehicleName = None

_websession = None
_require_refresh = False
_the_refresh_token = None
_web_session = None
_user = None
_pwd = None
_veh_index = 0

def __init__(self, user, pwd, websession, veh_index: int = 0, veh_id: str = None, options: dict = None):
if websession is not None:
def __init__(self, user, pwd, a_web_session, veh_index: int = 0, veh_id: str = None, options: dict = None):
if a_web_session is not None:
_LOGGER.info(f"restarting TibberGraphApi integration... for tibber-vehicle-id: '{veh_id}' vehicle-index: '{veh_index}' with options: {options}")
self._websession = websession
self._web_session = a_web_session
self._user = user
self._pwd = pwd
self._veh_index = veh_index
if veh_id is not None:
self.tibber_vehicleId = veh_id

async def update(self) -> dict:
return await self.get_vehicle_data(self._websession)
return await self.get_vehicle_data()

def should_be_refreshed(self) -> bool:
return self._require_refresh or "Authorization" not in self.REQ_HEADERS

async def login(self) -> None:
self.REQ_HEADERS["Content-Type"] = "application/x-www-form-urlencoded"
login_data = f"email={self._user}&password={self._pwd}"
async with self._websession.post(self.LOGIN_URL, data=login_data, headers=self.REQ_HEADERS) as response:
async with self._web_session.post(self.LOGIN_URL, data=login_data, headers=self.REQ_HEADERS) as response:
if response.status == 200:
data = await response.json()

Expand All @@ -275,34 +291,56 @@ async def login(self) -> None:
self.REQ_HEADERS["Authorization"] = data["token"]
if "refreshToken" in data:
_LOGGER.debug(f"refreshToken received")
self.a_refresh_token = data["refreshToken"]
self._the_refresh_token = data["refreshToken"]
self._require_refresh = False
else:
self.REQ_HEADERS["Content-Type"] = "application/json; charset=utf-8"
_LOGGER.error(f"{response.status} -> {response.reason}")

async def get_vehicle_id(self) -> str:
if "Authorization" not in self.REQ_HEADERS:
await self.login()

jdata = {
"query": "query getVehicles {me {myVehicles {vehicles {id, title} } } }"
}
async with self._websession.post(self.DATA_URL, json=jdata, headers=self.REQ_HEADERS) as response:
if response.status == 200:
data = await response.json()
if "data" in data \
and "me" in data["data"] \
and "myVehicles" in data["data"]["me"] \
and "vehicles" in data["data"]["me"]["myVehicles"] \
and len(data["data"]["me"]["myVehicles"]["vehicles"]) > self._veh_index:
self.tibber_vehicleId = data["data"]["me"]["myVehicles"]["vehicles"][self._veh_index]["id"]
self.tibber_vehicleName = data["data"]["me"]["myVehicles"]["vehicles"][self._veh_index]["title"]
_LOGGER.warning(f"login {response.status} -> {response.reason}")

async def refresh_token(self) -> None:
if self._the_refresh_token is not None:
self.REQ_HEADERS["Authorization"] = self._the_refresh_token
self.REQ_HEADERS["Content-Type"] = "application/json; charset=utf-8"
async with self._web_session.put(self.REFRESH_URL, headers=self.REQ_HEADERS) as response:
if response.status == 200:
ref_data = None
ref_text = None
if response.headers["Content-Type"] == "application/json":
ref_data = await response.json()
else:
# we try to parse the text as json ?!
ref_text = await response.text()
try:
ref_data = json.loads(ref_text)
except Exception as other:
_LOGGER.warning(f"could not parse refresh response: {other} {ref_text}")

if ref_data is not None and "token" in ref_data:
self._require_refresh = False
self.REQ_HEADERS["Authorization"] = ref_data["token"]
if "refreshToken" in ref_data:
_LOGGER.debug(f"refreshToken updated !")
self._the_refresh_token = ref_data["refreshToken"]
else:
_LOGGER.warning(f"not refreshToken provided !")
self._the_refresh_token = None
else:
_LOGGER.warning(f"no valid data in refresh token response: {ref_data} {ref_text}")
self._require_refresh = False
self.REQ_HEADERS.pop("Authorization")
else:
_LOGGER.error(f"Could not find vehicle id in response: {data}")
else:
_LOGGER.error(f"{response.status} -> {response.reason}")
_LOGGER.warning(f"refresh_token: {response.status} -> {response.reason}")
self._require_refresh = False
self.REQ_HEADERS.pop("Authorization")
else:
_LOGGER.warning(f"refresh token was called but the 'refresh_token' is NONE")
self._require_refresh = False
self.REQ_HEADERS.pop("Authorization")

async def get_vehicle_data(self) -> dict:
if self._require_refresh:
await self.refresh_token()

async def get_vehicle_data(self, session) -> dict:
if "Authorization" not in self.REQ_HEADERS:
await self.login()

Expand All @@ -319,51 +357,48 @@ async def get_vehicle_data(self, session) -> dict:
"} } }"
}

async with session.post(self.DATA_URL, json=jdata, headers=self.REQ_HEADERS) as response:
async with self._web_session.post(self.DATA_URL, json=jdata, headers=self.REQ_HEADERS) as response:
if response.status == 401:
_LOGGER.debug(f"401 received - trying to refresh auth token")
if self.a_refresh_token is None:
_LOGGER.warning(f"no refresh token available")
await self.login()
return await self.get_vehicle_data(session)
if self._the_refresh_token is None:
_LOGGER.warning(f"no refresh token available - wait for next call to re-login...")
self._require_refresh = False
self.REQ_HEADERS.pop("Authorization")
else:
self.REQ_HEADERS["Authorization"] = self.a_refresh_token
self.REQ_HEADERS["Content-Type"] = "application/json; charset=utf-8"
async with session.put(self.REFRESH_URL, json=jdata, headers=self.REQ_HEADERS) as refresh_response:
if refresh_response.status == 200:
ref_data = None
ref_text = None
if refresh_response.headers["Content-Type"] == "application/json":
ref_data = await refresh_response.json()
else:
# we try to parse the text as json ?!
ref_text = await refresh_response.text()
try:
ref_data = json.loads(ref_text)
except Exception as other:
_LOGGER.warning(f"could not parse refresh response: {other} {ref_text}")

if ref_data is not None and "token" in ref_data:
self.REQ_HEADERS["Authorization"] = ref_data["token"]
if "refreshToken" in ref_data:
_LOGGER.debug(f"refreshToken updated !")
self.a_refresh_token = ref_data["refreshToken"]
else:
_LOGGER.error(f"no valid data in refresh token response: {ref_data} {ref_text}")
await self.login()
else:
await self.login()

return await self.get_vehicle_data(session)
_LOGGER.debug(f"refresh token available... try to refresh next...")
self._require_refresh = True

elif response.status == 200:
data = await response.json()
if "data" in data and "me" in data["data"] and "vehicle" in data["data"]["me"]:
return data["data"]["me"]["vehicle"]
else:
_LOGGER.warning(f"get_vehicle_data {response.status} -> {response.reason}")

# if we haven't read any data (cause of 401 or other status) we return None
return None

async def get_vehicle_id(self) -> str:
if "Authorization" not in self.REQ_HEADERS:
await self.login()

jdata = {
"query": "query getVehicles {me {myVehicles {vehicles {id, title} } } }"
}
async with self._web_session.post(self.DATA_URL, json=jdata, headers=self.REQ_HEADERS) as response:
if response.status == 200:
data = await response.json()
if "data" in data \
and "me" in data["data"] \
and "myVehicles" in data["data"]["me"] \
and "vehicles" in data["data"]["me"]["myVehicles"] \
and len(data["data"]["me"]["myVehicles"]["vehicles"]) > self._veh_index:
self.tibber_vehicleId = data["data"]["me"]["myVehicles"]["vehicles"][self._veh_index]["id"]
self.tibber_vehicleName = data["data"]["me"]["myVehicles"]["vehicles"][self._veh_index]["title"]
else:
return None
_LOGGER.warning(f"Could not find vehicle id in response: {data}")
else:
_LOGGER.error(f"{response.status} -> {response.reason}")
_LOGGER.warning(f"get_vehicle_id {response.status} -> {response.reason}")

@property
def vehicle_id(self):
Expand Down
2 changes: 1 addition & 1 deletion custom_components/tibber_graphapi/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def __init__(self):
async def _test_connection_tibber_graphapi(self, user, pwd, veh_idx) -> bool:
self._errors = {}
try:
bridge = TibberGraphApiBridge(user=user, pwd=pwd, websession=async_get_clientsession(self.hass), veh_index=veh_idx)
bridge = TibberGraphApiBridge(user=user, pwd=pwd, a_web_session=async_get_clientsession(self.hass), veh_index=veh_idx)
try:
await bridge.get_vehicle_id()
self._tibber_veh_id = bridge.vehicle_id
Expand Down

0 comments on commit 493e657

Please sign in to comment.