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
204 lines • 7.84 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.graphqlApi = exports.hiloApi = exports.getWsAccessToken = exports.getAccessToken = void 0;
exports.negotiate = negotiate;
exports.setupAutoRefreshToken = setupAutoRefreshToken;
exports.execute = execute;
const axios_1 = __importStar(require("axios"));
const fs_1 = __importDefault(require("fs"));
const jsonwebtoken_1 = require("jsonwebtoken");
const config_1 = require("./config");
const logger_1 = require("./logger");
const api_1 = require("./api");
let accessToken;
let refreshToken;
let wsAccessToken;
const clientId = "1ca9f585-4a55-4085-8e30-9746a65fa561";
const authServer = "https://connexion.hiloenergie.com";
const renewTokens = async ({ newRefreshToken, newAccessToken, expiresIn, }) => {
const configPath = (0, api_1.getApi)().user.configPath();
const config = JSON.parse(fs_1.default.readFileSync(configPath, "utf8"));
const hilo = config.platforms.find((p) => p.platform === "Hilo");
hilo.refreshToken = newRefreshToken;
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 4));
accessToken = newAccessToken;
refreshToken = newRefreshToken;
await setupAutoRefreshToken(expiresIn);
};
async function login() {
(0, logger_1.getLogger)().debug("Logging in");
const config = (0, config_1.getConfig)();
const newRefreshToken = config.refreshToken;
await renewTokens({
newRefreshToken,
});
}
async function refreshTokenRequest() {
(0, logger_1.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_1.default.post(`${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,
});
}
const getAccessToken = () => accessToken || "";
exports.getAccessToken = getAccessToken;
const getWsAccessToken = () => wsAccessToken || "";
exports.getWsAccessToken = getWsAccessToken;
let wsRefreshTimer;
async function negotiate() {
(0, logger_1.getLogger)().debug("Negotiating websocket connection");
const response = await exports.hiloApi.post("/DeviceHub/negotiate", {}, {
params: {
negociateVersion: 1,
},
});
wsAccessToken = response.data.accessToken;
if (wsRefreshTimer) {
clearTimeout(wsRefreshTimer);
}
const decoded = (0, jsonwebtoken_1.decode)(response.data.accessToken);
const exp = decoded === null || decoded === void 0 ? void 0 : decoded.exp;
// Refresh 5 minutes before expiration, fallback to 55 minutes if exp is unreadable
const refreshIn = exp
? exp * 1000 - Date.now() - 5 * 60 * 1000
: 55 * 60 * 1000;
(0, logger_1.getLogger)().debug(`WebSocket token refreshing in ${Math.round(refreshIn / 60000)} minutes`);
wsRefreshTimer = setTimeout(async () => {
(0, logger_1.getLogger)().debug("Refreshing websocket token");
try {
await negotiate();
}
catch (e) {
(0, logger_1.getLogger)().error("Failed to refresh websocket token", e instanceof Error ? e.message : e);
}
}, Math.max(refreshIn, 60000));
return { accessToken: response.data.accessToken, url: response.data.url };
}
async function setupAutoRefreshToken(expiresIn) {
if (!expiresIn) {
(0, logger_1.getLogger)().debug("Refreshing token");
try {
await refreshTokenRequest();
}
catch (e) {
unableToLogin(e);
}
}
else {
(0, logger_1.getLogger)().debug("Setting up auto refresh token");
setTimeout(async () => {
(0, logger_1.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
}
}
exports.hiloApi = axios_1.default.create({
baseURL: "https://api.hiloenergie.com",
});
exports.graphqlApi = axios_1.default.create({
baseURL: "https://platform.hiloenergie.com",
});
function execute(query, ...[variables]) {
return exports.graphqlApi.post("/api/digital-twin/v3/graphql", {
query: query.toString(),
variables,
}, {
headers: {
"Content-Type": "application/json",
},
});
}
const authInterceptor = async (config) => {
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 axios_1.AxiosHeaders({
...config.headers,
"Ocp-Apim-Subscription-Key": "20eeaedcb86945afa3fe792cea89b8bf",
Authorization: accessToken ? `Bearer ${accessToken}` : "",
});
return config;
};
exports.hiloApi.interceptors.request.use(authInterceptor);
exports.graphqlApi.interceptors.request.use(authInterceptor);
const unableToLogin = (e) => {
var _a;
const logger = (0, logger_1.getLogger)();
logger.error("Unable to login", axios_1.default.isAxiosError(e) ? (_a = e.response) === null || _a === void 0 ? void 0 : _a.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");
};
//# sourceMappingURL=hiloApi.js.map