UNPKG

homebridge-august-locks-jstroins

Version:

Updated August Smart Lock plugin for Homebridge with fixes and JSON parsing improvements.

199 lines (192 loc) 7.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.augustSetStatus = exports.augustGetLockStatus = exports.augustGetLocks = exports.augustGetHouses = exports.augustStartSession = exports.AugustLockStatus = void 0; const https_1 = require("https"); const util_1 = require("util"); var AugustLockStatus; (function (AugustLockStatus) { AugustLockStatus[AugustLockStatus["LOCKED"] = 0] = "LOCKED"; AugustLockStatus[AugustLockStatus["UNLOCKED"] = 1] = "UNLOCKED"; AugustLockStatus[AugustLockStatus["UNKNOWN"] = 2] = "UNKNOWN"; })(AugustLockStatus = exports.AugustLockStatus || (exports.AugustLockStatus = {})); async function augustStartSession(options, log) { const { uuid, code, idType, password, identifier } = options; const session = await augustLogin(uuid, idType, identifier, password, log); log.debug(JSON.stringify(session)); const { status } = await augustGetMe(session, log); // Session isn't valid yet, so we need to validate it by sending a code if (status !== 200) { if (code === undefined || code.length === 0) { await augustSendCode(session, log); log.info('Session is not valid yet, but no code was provided. Please provide a code.'); } else { const resp = await augustValidateCode(code, session, log); if (resp.status !== 200) { await augustSendCode(session, log); log.error(`Invalid code: ${resp.status}, new code sent, please provide the new code.`); } else { session.token = resp.token; } } } return session; } exports.augustStartSession = augustStartSession; function getRequestOptions(path, method) { return { hostname: 'api-production.august.com', port: 443, path: path, method: method, headers: { 'x-august-api-key': '7cab4bbd-2693-4fc1-b99b-dec0fb20f9d4', 'x-kease-api-key': '7cab4bbd-2693-4fc1-b99b-dec0fb20f9d4', 'Content-Type': 'application/json', 'Accept-Version': '0.0.1', 'User-Agent': 'August/Luna-3.2.2', }, }; } function addToken(options, token) { const newOptions = { ...options, }; if (newOptions['headers']) { newOptions['headers']['x-august-access-token'] = token; } return newOptions; } async function makeRequest(options, data, log) { return new Promise((resolve, reject) => { const req = (0, https_1.request)(options, res => { let body = ''; res.on('data', chunk => { body += chunk.toString('utf-8'); }); res.on('end', () => { log.debug(`statusCode: ${res.statusCode}`); log.debug('Raw API response:', body); let payload; try { payload = JSON.parse(body); } catch (err) { log.error('Failed to parse response as JSON:', body); return reject(err); } resolve({ status: res.statusCode, token: res.headers['x-august-access-token'], payload: payload, }); }); }); req.on('error', error => { log.error(`request error: ${error}`); reject(error); }); req.write(data); req.end(); }); } async function augustLogin(uuid, idType, identifier, password, log) { const data = new util_1.TextEncoder().encode(JSON.stringify({ identifier: `${idType}:${identifier}`, password: password, installId: uuid, })); const options = getRequestOptions('/session', 'POST'); const res = await makeRequest(options, data, log); if (res.status !== 200 || res.payload['userId'] === undefined || res.payload['userId'].length === 0) { throw new Error(`Invalid user credentials: ${res.status}`); } else { return { idType: idType, identifier: identifier, token: res.token, }; } } async function augustGetMe(session, log) { const options = addToken(getRequestOptions('/users/me', 'GET'), session.token); return makeRequest(options, new util_1.TextEncoder().encode(''), log); } async function augustSendCode(session, log) { const data = new util_1.TextEncoder().encode(JSON.stringify({ value: session.identifier, })); const options = addToken(getRequestOptions(`/validation/${session.idType}`, 'POST'), session.token); return makeRequest(options, data, log); } async function augustValidateCode(code, session, log) { const payload = { code, }; payload[session.idType] = session.identifier; const data = new util_1.TextEncoder().encode(JSON.stringify(payload)); const options = addToken(getRequestOptions(`/validate/${session.idType}`, 'POST'), session.token); return makeRequest(options, data, log); } async function augustGetHouses(session, log) { const options = addToken(getRequestOptions('/users/houses/mine', 'GET'), session.token); const results = await makeRequest(options, new Uint8Array(), log); if (results.status === 200 && Array.isArray(results.payload)) { const homes = (results.payload).map(home => ({ id: home['HouseID'], name: home['HouseName'], })); return homes; } else { return []; } } exports.augustGetHouses = augustGetHouses; async function augustGetLocks(session, log) { const options = addToken(getRequestOptions('/users/locks/mine', 'GET'), session.token); const results = await makeRequest(options, new Uint8Array(), log); if (results.status === 200 && results.payload) { const locks = Object.keys(results.payload).map(id => { const lock = results.payload[id]; return { id: id, name: lock['LockName'], macAddress: lock['macAddress'], houseId: lock['HouseID'], houseName: lock['HouseName'], }; }); return locks; } else { return []; } } exports.augustGetLocks = augustGetLocks; async function augustGetLockStatus(session, lockId, log) { const options = addToken(getRequestOptions(`/remoteoperate/${lockId}/status`, 'PUT'), session.token); const results = await makeRequest(options, new Uint8Array(), log); const status = results.payload['status']; if (status === 'kAugLockState_Locked') { return AugustLockStatus.LOCKED; } else if (status === 'kAugLockState_Unlocked') { return AugustLockStatus.UNLOCKED; } else { log.info(JSON.stringify(results.payload)); throw new Error(`Unknown lock status for lock ${lockId}`); } } exports.augustGetLockStatus = augustGetLockStatus; async function augustSetStatus(session, lockId, status, log) { const url = status === AugustLockStatus.LOCKED ? `/remoteoperate/${lockId}/lock` : `/remoteoperate/${lockId}/unlock`; const options = addToken(getRequestOptions(url, 'PUT'), session.token); const results = await makeRequest(options, new Uint8Array(), log); const update = results.payload['status'] === 'kAugLockState_Locked' ? AugustLockStatus.LOCKED : AugustLockStatus.UNLOCKED; return update; } exports.augustSetStatus = augustSetStatus; //# sourceMappingURL=august.js.map