UNPKG

homebridge-hilo

Version:

Plugin Homebridge (non officiel) pour la passerelle et les appareils Hilo de Hydro-Québec | Unofficial Homebridge plugin for Hydro-Québec Hilo bridge and devices

176 lines (159 loc) 4.7 kB
import axios, { AxiosHeaders, InternalAxiosRequestConfig } from "axios"; import fs from "fs"; import { getConfig } from "./config"; import { getLogger } from "./logger"; import { getApi } from "./api"; import { TypedDocumentString } from "./graphql/graphql"; let accessToken: string | undefined; let refreshToken: string | undefined; const clientId = "1ca9f585-4a55-4085-8e30-9746a65fa561"; const authServer = "https://connexion.hiloenergie.com"; const renewTokens = async ({ newRefreshToken, newAccessToken, expiresIn, }: { newRefreshToken: string; newAccessToken?: string; expiresIn?: number; }) => { const configPath = getApi().user.configPath(); const config = JSON.parse(fs.readFileSync(configPath, "utf8")); const hilo = config.platforms.find( (p: { platform: string }) => p.platform === "Hilo", ); hilo.refreshToken = newRefreshToken; fs.writeFileSync(configPath, JSON.stringify(config, null, 4)); accessToken = newAccessToken; refreshToken = newRefreshToken; await setupAutoRefreshToken(expiresIn); }; async function login() { getLogger().debug("Logging in"); const config = getConfig(); const newRefreshToken = config.refreshToken; await renewTokens({ newRefreshToken, }); } type RefreshTokenResponse = { access_token: string; id_token: string; token_type: "Bearer"; not_before: number; expires_in: number; resource: string; id_token_expires_in: number; profile_info: string; scope: string; refresh_token: string; refresh_token_expires_in: number; }; async function refreshTokenRequest() { getLogger().debug("Refreshing token"); const data = new URLSearchParams(); data.append("grant_type", "refresh_token"); data.append("client_id", clientId); data.append("refresh_token", refreshToken!); data.append( "scope", "openid https://HiloDirectoryB2C.onmicrosoft.com/hiloapis/user_impersonation offline_access", ); data.append("redirect_uri", "https://my.home-assistant.io/redirect/oauth"); const response = await axios.post<RefreshTokenResponse>( `${authServer}/HiloDirectoryB2C.onmicrosoft.com/B2C_1A_SIGN_IN/oauth2/v2.0/token?p=b2c_1a_sign_in`, data, ); await renewTokens({ newAccessToken: response.data.access_token, newRefreshToken: response.data.refresh_token, expiresIn: response.data.expires_in, }); } export const getAccessToken = () => accessToken || ""; export async function setupAutoRefreshToken(expiresIn: number | undefined) { if (!expiresIn) { getLogger().debug("Refreshing token"); try { await refreshTokenRequest(); } catch (e) { unableToLogin(e); } } else { getLogger().debug("Setting up auto refresh token"); setTimeout( async () => { getLogger().debug("Refreshing token automatically"); try { await refreshTokenRequest(); } catch (_e) { try { await login(); } catch (e) { unableToLogin(e); } } }, expiresIn * 1000 - 1000 * 60 * 5, ); // 5 minutes before expiration } } export const hiloApi = axios.create({ baseURL: "https://api.hiloenergie.com", }); export const graphqlApi = axios.create({ baseURL: "https://platform.hiloenergie.com", }); export function execute<TResult, TVariables>( query: TypedDocumentString<TResult, TVariables>, ...[variables]: TVariables extends Record<string, never> ? [] : [TVariables] ) { return graphqlApi.post( "/api/digital-twin/v3/graphql", { query: query.toString(), variables, }, { headers: { "Content-Type": "application/json", }, }, ); } const authInterceptor = async (config: InternalAxiosRequestConfig) => { if (!accessToken) { if (refreshToken) { try { await refreshTokenRequest(); } catch (_e) { try { await login(); } catch (e) { unableToLogin(e); } } } else { try { await login(); } catch (e) { unableToLogin(e); } } } config.headers = new AxiosHeaders({ ...config.headers, "Ocp-Apim-Subscription-Key": "20eeaedcb86945afa3fe792cea89b8bf", Authorization: accessToken ? `Bearer ${accessToken}` : "", }); return config; }; hiloApi.interceptors.request.use(authInterceptor); graphqlApi.interceptors.request.use(authInterceptor); const unableToLogin = (e: unknown) => { const logger = getLogger(); logger.error("Unable to login", axios.isAxiosError(e) ? e.response?.data : e); logger.error( "You can try to refresh your login credentials by using the 'Login with Hilo' button in the plugin configuration in homebridge UI", ); };