UNPKG

homebridge-hatch-baby-rest

Version:
184 lines (183 loc) 8.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HatchBabyApi = void 0; const rest_client_1 = require("./rest-client"); const aws_iot_device_sdk_1 = require("aws-iot-device-sdk"); const util_1 = require("../shared/util"); const rest_plus_1 = require("./rest-plus"); const rest_iot_1 = require("./rest-iot"); const rest_mini_1 = require("./rest-mini"); const restore_1 = require("./restore"); const rxjs_1 = require("rxjs"); const iot_device_1 = require("./iot-device"); const operators_1 = require("rxjs/operators"); const knownProducts = [ "restPlus" /* Product.restPlus */, "riot" /* Product.riot */, "riotPlus" /* Product.riotPlus */, "restMini" /* Product.restMini */, "restore" /* Product.restore */, "restoreIot" /* Product.restoreIot */, ], ignoredProducts = [ "rest" /* Product.rest */, "alexa" /* Product.alexa */, "grow" /* Product.grow */, "answeredReader" /* Product.answeredReader */, ], iotClientRefreshPeriod = 8 * 60 * 60 * 1000; // refresh client every 8 hours class HatchBabyApi { constructor(config) { this.config = config; this.restClient = new rest_client_1.RestClient(this.config); } getAccount() { return this.restClient.getAccount(); } getMember() { return this.restClient.request({ url: (0, rest_client_1.apiPath)('service/app/v2/member'), }); } async getIotDevices(...products) { // Combine the user's products with the known products to ensure we get all devices const productFetchQueryString = products .map((product) => 'iotProducts=' + product) .join('&'), devices = (await this.restClient.request({ url: (0, rest_client_1.apiPath)('service/app/iotDevice/v2/fetch?' + productFetchQueryString), })) || []; return devices; } async createAwsIotClient() { const iotResponse = await this.restClient.request({ url: (0, rest_client_1.apiPath)('service/app/restPlus/token/v1/fetch'), }), { Credentials: credentials } = await (0, rest_client_1.requestWithRetry)({ url: `https://cognito-identity.${iotResponse.region}.amazonaws.com`, method: 'POST', headers: { 'content-type': 'application/x-amz-json-1.1', 'X-Amz-Target': 'AWSCognitoIdentityService.GetCredentialsForIdentity', }, json: { IdentityId: iotResponse.identityId, Logins: { 'cognito-identity.amazonaws.com': iotResponse.token, }, }, }), mqttClient = new aws_iot_device_sdk_1.thingShadow({ protocol: 'wss', host: iotResponse.endpoint.replace('https://', ''), accessKeyId: credentials.AccessKeyId, secretKey: credentials.SecretKey, sessionToken: credentials.SessionToken, }); return mqttClient; } async getOnIotClient() { // eslint-disable-next-line prefer-const let onIotClient; const createNewIotClient = async () => { try { // eslint-disable-next-line no-use-before-define const previousMqttClient = onIotClient?.getValue(); if (previousMqttClient) { try { previousMqttClient.end(); } catch (e) { (0, util_1.logError)('Failed to end previous MQTT Client'); (0, util_1.logError)(e); } } (0, util_1.logDebug)('Creating new MQTT Client'); const mqttClient = await this.createAwsIotClient(); mqttClient.on('error', async (error) => { if (error.message.includes('(403)')) { (0, util_1.logError)('MQTT Client No Longer Authorized'); } else { (0, util_1.logError)('MQTT Error:'); (0, util_1.logError)(error); } try { // eslint-disable-next-line no-use-before-define onIotClient?.next(await createNewIotClient()); } catch (_) { // ignore, already logged } }); (0, util_1.logDebug)('Created new MQTT Client'); return mqttClient; } catch (e) { (0, util_1.logError)('Failed to Create an MQTT Client'); (0, util_1.logError)(e); throw e; } }; onIotClient = new rxjs_1.BehaviorSubject(await createNewIotClient()); onIotClient.pipe((0, operators_1.debounceTime)(iotClientRefreshPeriod)).subscribe(() => { createNewIotClient() .then((client) => onIotClient?.next(client)) .catch(util_1.logError); }); return onIotClient; } async getDevices() { const [devices, member, onIotClient] = await Promise.all([ this.getIotDevices(...knownProducts), this.getMember(), this.getOnIotClient(), ]), createDevices = (product, Device) => { return devices .filter((device) => device.product === product) .map((info) => new Device(info, onIotClient, this.restClient)); }; for (const product of member.products) { if (!knownProducts.includes(product) && !ignoredProducts.includes(product)) { const debugMessage = this.config.debug ? '' : '. Set `"debug": true` in your config to see more device details.'; (0, util_1.logInfo)('Unsupported Product Found: ' + product + debugMessage); if (this.config.debug) { this.getIotDevices(product) .then(async (unknownDevices) => { const debugDevices = unknownDevices.map((device) => new iot_device_1.IotDevice(device, onIotClient)); for (const device of debugDevices) { try { (0, util_1.logInfo)(`Debug info for ${product} ${device.info.name}:`); (0, util_1.logInfo)(JSON.stringify({ ...device.info, id: '***', macAddress: '***', thingName: '***', memberId: '***', email: '***', }, null, 2)); (0, util_1.logInfo)(`State for ${product} ${device.info.name}:`); (0, util_1.logInfo)(JSON.stringify(await device.getCurrentState(), null, 2)); } catch (e) { (0, util_1.logError)(`Failed to get debug info for ${product} ${device.info.name}`); (0, util_1.logError)(e); } } }) .catch((error) => { (0, util_1.logError)('Failed to get debug info for ' + product); (0, util_1.logError)(error); }); } } } return { restPluses: createDevices("restPlus" /* Product.restPlus */, rest_plus_1.RestPlus), restIots: createDevices("riot" /* Product.riot */, rest_iot_1.RestIot), restIotPluses: createDevices("riotPlus" /* Product.riotPlus */, rest_iot_1.RestIot), restMinis: createDevices("restMini" /* Product.restMini */, rest_mini_1.RestMini), restores: createDevices("restore" /* Product.restore */, restore_1.Restore), restoreIots: createDevices("restoreIot" /* Product.restoreIot */, rest_iot_1.RestIot), }; } } exports.HatchBabyApi = HatchBabyApi;