millheat-js
Version:
Javascript client to ease the use of the Mill Open API V2
144 lines (139 loc) • 3.99 kB
JavaScript
import fetch, { Headers } from 'node-fetch';
import { Agent } from 'https';
class Authenticator {
constructor(credentials, apiURL, agent) {
this.credentials = credentials;
this.apiURL = apiURL;
this.agent = agent;
}
status = { type: "initial" };
async authenticate() {
let accessToken = await this.retrieveAccessToken();
return [["Authorization", `Bearer ${accessToken.idToken}`]];
}
async retrieveAccessToken() {
switch (this.status.type) {
case "authenticating":
return await this.status.result;
case "authenticated":
if (Date.now() < this.status.accessToken.expireTime) {
return this.status.accessToken;
}
}
let result = this.refresh();
this.status = { type: "authenticating", result };
try {
let accessToken = await result;
this.status = { type: "authenticated", accessToken };
return accessToken;
} catch (error) {
this.status = { type: "failed", error };
return Promise.reject(error);
}
}
async refresh() {
let response = await fetch(`${this.apiURL}/customer/auth/sign-in
`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
login: this.credentials.username,
password: this.credentials.password
}),
agent: this.agent
});
let payload = await response.json();
try {
let parts = payload.idToken.split(".");
let decoded = JSON.parse(Buffer.from(parts[1], "base64").toString());
return {
...payload,
expireTime: Date.now() + decoded.exp - decoded.iat,
refreshExpireTime: Date.now() + decoded.exp - decoded.iat
};
} catch (error) {
return Promise.reject(error);
}
}
}
async function execute(input, init, request, timeout) {
const AbortController = globalThis.AbortController || (await import('abort-controller')).default;
let cancelController = new AbortController();
let cancelTimeout = setTimeout(() => {
cancelController.abort();
}, timeout);
let headers = new Headers(init.headers);
let requestInit = {
...init,
body: request ? JSON.stringify(request) : void 0,
method: request ? "POST" : "GET",
signal: cancelController.signal,
headers
};
try {
let response = await fetch(input, requestInit);
if (response.status === 200) {
let payload = await response.json();
return payload;
}
return Promise.reject(new Error("Unexpected error"));
} catch (error) {
return Promise.reject(error);
} finally {
clearTimeout(cancelTimeout);
}
}
const apiUrl = "https://api.millnorwaycloud.com";
class MillheatAPI {
options;
agent = new Agent({ keepAlive: true });
constructor(credential, options = {}) {
this.options = {
timeout: 3e4,
...options
};
this.credential = credential;
this.authenticator = new Authenticator(credential, apiUrl, this.agent);
}
async api(input, init, request) {
let headers = new Headers(init ? init.headers : void 0);
let authenticationHeaders = await this.authenticator.authenticate();
for (let [key, value] of authenticationHeaders) {
headers.append(key, value);
}
let requestInit = {
...init,
agent: this.agent,
headers
};
return execute(input, requestInit, request, this.options.timeout);
}
async getHouses() {
let response = await this.api(
`${apiUrl}/houses`,
{},
null
);
return response;
}
async getDevicesForHouse(houseId) {
let response = await this.api(
`${apiUrl}/houses/${houseId}/devices`,
{},
null
);
return response;
}
async getIndependentDevicesForHouse(houseId) {
let response = await this.api(
`${apiUrl}/houses/${houseId}/devices/independent`,
{},
null
);
return response;
}
}
export { MillheatAPI as Client };
//# sourceMappingURL=millheat-js.mjs.map