UNPKG

yale-doorman

Version:

SDK for Yale Doorman through the Yale cloud API

201 lines (200 loc) 10.9 kB
"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _YaleDoorman_accessToken, _YaleDoorman_refreshToken, _YaleDoorman_accessTokenExpiresAt, _YaleDoorman_configuration, _YaleDoorman_email, _YaleDoorman_password; Object.defineProperty(exports, "__esModule", { value: true }); exports.YaleDoorman = void 0; const deepMerge_js_1 = require("./misc/deepMerge.js"); const default_js_1 = __importDefault(require("./config/default.js")); const url_1 = require("url"); const assert_1 = __importDefault(require("assert")); const got_1 = __importDefault(require("got")); /** * SDK class that expose the Yale doorman SDK. * * Note that `login()` does not need to be called explicitly. * The SDK will lazily call `login()` to (re)authenticate when necessary. */ class YaleDoorman { /** The generic type parameter should be either omitted or `false` in production. */ constructor(email, password, configuration) { _YaleDoorman_accessToken.set(this, void 0); _YaleDoorman_refreshToken.set(this, void 0); _YaleDoorman_accessTokenExpiresAt.set(this, void 0); _YaleDoorman_configuration.set(this, void 0); _YaleDoorman_email.set(this, void 0); _YaleDoorman_password.set(this, void 0); __classPrivateFieldSet(this, _YaleDoorman_configuration, (!configuration ? default_js_1.default : (0, deepMerge_js_1.deepMerge)(default_js_1.default, configuration)), "f"); __classPrivateFieldSet(this, _YaleDoorman_email, email, "f"); __classPrivateFieldSet(this, _YaleDoorman_password, password, "f"); } /** Send a REST request to the Sector API */ async httpRequest({ endpoint, method = "GET", form, isRetry = false, query }) { if (isRetry || !__classPrivateFieldGet(this, _YaleDoorman_accessTokenExpiresAt, "f") || (__classPrivateFieldGet(this, _YaleDoorman_accessTokenExpiresAt, "f").valueOf() - __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").clock.Date.now()) < 1000 * 60) { await this.login(); } (0, assert_1.default)(__classPrivateFieldGet(this, _YaleDoorman_accessToken, "f"), "Expect access token to be defined"); const { yale: { host, endpoints } } = __classPrivateFieldGet(this, _YaleDoorman_configuration, "f"); const response = await (0, got_1.default)(new url_1.URL(endpoints[endpoint], host), { method, form, searchParams: query, headers: { authorization: `Bearer ${__classPrivateFieldGet(this, _YaleDoorman_accessToken, "f")}` }, resolveBodyOnly: false, throwHttpErrors: false, retry: { limit: 3 }, responseType: "json" }); __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").logger.debug(`Request ${method} ${endpoints[endpoint]} -> HTTP${response.statusCode}`); if (response.statusCode === 401 && !isRetry) { return await this.httpRequest({ endpoint, method, form, isRetry: true, query }); } else if (response.statusCode === 401) { throw new Error(`Authentication error (${method}). Body: ${response.rawBody.toString()}`); } else if (response.statusCode >= 400) { throw new Error(`HTTP error ${method} ${endpoint}: ${response.statusCode} ${response.rawBody.toString()}`); } return response.body; } /** Returns a boolean indicating success */ async loginWithRefreshToken() { if (!__classPrivateFieldGet(this, _YaleDoorman_refreshToken, "f")) { return false; } const { yale: { host, endpoints: { token } } } = __classPrivateFieldGet(this, _YaleDoorman_configuration, "f"); // Perform the actual login const response = await (0, got_1.default)(new url_1.URL(token, host), { method: "POST", resolveBodyOnly: false, throwHttpErrors: false, username: __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").yale.clientId, password: __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").yale.clientSecret, form: { grant_type: "refresh_token", refresh_token: __classPrivateFieldGet(this, _YaleDoorman_refreshToken, "f") }, responseType: "json", retry: { limit: 3 } }); if (response.statusCode < 300) { __classPrivateFieldSet(this, _YaleDoorman_accessToken, response.body.access_token, "f"); __classPrivateFieldSet(this, _YaleDoorman_accessTokenExpiresAt, new Date(response.body.expires_in * 1000), "f"); __classPrivateFieldSet(this, _YaleDoorman_refreshToken, response.body.refresh_token, "f"); __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").logger.info("Successfully authenticated with Yale API using refresh token"); return true; } __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").logger.debug("Failed to authenticate with using refresh token"); return false; } /** Authenticate using the credentials specified in the constructor. * * This function is used internally and does not need to be called directly. */ async login() { if (await this.loginWithRefreshToken()) { return; } const { yale: { host, endpoints: { token } } } = __classPrivateFieldGet(this, _YaleDoorman_configuration, "f"); // Perform the actual login const response = await (0, got_1.default)(new url_1.URL(token, host), { method: "POST", resolveBodyOnly: false, throwHttpErrors: false, username: __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").yale.clientId, password: __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").yale.clientSecret, form: { grant_type: "password", username: __classPrivateFieldGet(this, _YaleDoorman_email, "f"), password: __classPrivateFieldGet(this, _YaleDoorman_password, "f") }, responseType: "json", retry: { limit: 3 } }); if (response.statusCode === 401) { throw new Error(`Authentication error on login. Maybe the credentials are incorrect? Body: ${response.rawBody.toString()}`); } else if (response.statusCode >= 400) { throw new Error(`HTTP error: ${response.statusCode} ${response.rawBody.toString()}`); } __classPrivateFieldSet(this, _YaleDoorman_accessToken, response.body.access_token, "f"); __classPrivateFieldSet(this, _YaleDoorman_accessTokenExpiresAt, new Date(response.body.expires_in * 1000), "f"); __classPrivateFieldSet(this, _YaleDoorman_refreshToken, response.body.refresh_token, "f"); __classPrivateFieldGet(this, _YaleDoorman_configuration, "f").logger.info("Successfully authenticated with Yale API using credentials"); } /** Fetch a list of devices connected to the Yale hub. */ async getDevices() { return this.httpRequest({ endpoint: "getDevices" }); } /** Convenience method to filter the set of Yale devices down the only doors. Only returns the metadata required for changing door states. */ async getDoors() { const devices = await this.httpRequest({ endpoint: "getDevices" }); const doors = devices.data .filter(device => device.type === "device_type.door_lock") .map(({ address, area, no, name, status_open }) => ({ name, zone: no, area, address, state: status_open === null || status_open === void 0 ? void 0 : status_open[0] })); return doors; } /** Fetch events from the Yale hub. */ async getEventHistory() { return this.httpRequest({ endpoint: "getEventHistory" }); } /** Locks a specific door. This request tends to take ~10 sec. The Zone, Area and RfAddress arguments may be retrieved using the getDevices() method. */ async lockDoor(zone, area, rfAddress) { const response = await this.httpRequest({ endpoint: "lockDoor", method: "POST", form: { area, zone, device_sid: rfAddress, device_type: "device_type.door_lock", request_value: "1" } }); if (response.code !== "000") { throw new Error(`Failed to lock the door. Response code: ${response.code} Body: ${JSON.stringify(response)}`); } } /** Unlocks a specific door. This request tends to take ~10 sec. The Zone and Area arguments may be retrieved using the getDevices() method. */ async unlockDoor(zone, area, pincode) { const response = await this.httpRequest({ endpoint: "unlockDoor", method: "POST", form: { area, zone, pincode } }); if (response.code === "996") { throw new Error(`Failed to unlock the door. The door pin might be incorrect. Response body: ${JSON.stringify(response)}`); } else if (response.code !== "000") { throw new Error(`Failed to unlock the door. Response code: ${response.code} Body: ${JSON.stringify(response)}`); } } } exports.YaleDoorman = YaleDoorman; _YaleDoorman_accessToken = new WeakMap(), _YaleDoorman_refreshToken = new WeakMap(), _YaleDoorman_accessTokenExpiresAt = new WeakMap(), _YaleDoorman_configuration = new WeakMap(), _YaleDoorman_email = new WeakMap(), _YaleDoorman_password = new WeakMap();