homebridge-august-door-sense
Version:
Homebridge Plugin for August DoorSense.
212 lines • 8.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.augustGetSerialNumber = exports.augustGetDoorStatus = exports.augustGetLocks = exports.augustGetHouses = exports.augustStartSession = exports.AugustDoorStatus = void 0;
const https_1 = require("https");
const util_1 = require("util");
var AugustDoorStatus;
(function (AugustDoorStatus) {
AugustDoorStatus[AugustDoorStatus["UNKNOWN"] = 0] = "UNKNOWN";
AugustDoorStatus[AugustDoorStatus["CLOSED"] = 1] = "CLOSED";
AugustDoorStatus[AugustDoorStatus["OPEN"] = 2] = "OPEN";
})(AugustDoorStatus || (exports.AugustDoorStatus = AugustDoorStatus = {}));
async function augustStartSession(options, log) {
const { code } = options;
const session = await augustLogin(options, 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) {
await augustSendCode(session, log);
log.info('Session is not valid yet, but no 2FA code was provided. Please provide a 2FA 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 2FA code.`);
}
else {
session.token = resp.token;
}
}
}
return session;
}
exports.augustStartSession = augustStartSession;
function getRequestOptions(apiKey, path, method, session) {
const options = {
hostname: 'api-production.august.com',
port: 443,
path: path,
method: method,
headers: {
'x-august-api-key': apiKey,
'x-kease-api-key': apiKey,
'Content-Type': 'application/json',
'Accept-Version': '0.0.1',
'User-Agent': 'August/Luna-3.2.2',
},
};
if (session) {
options['headers']['x-august-access-token'] = session.token;
}
return options;
}
async function makeRequest(options, data, log) {
return new Promise((resolve) => {
const req = (0, https_1.request)(options, res => {
const chunks = [];
res.on('data', d => {
log.debug(`statusCode: ${res.statusCode}`);
const buff = d;
const buffStr = buff.toString('utf8');
log.debug(buffStr);
chunks.push(buffStr);
});
res.on('end', () => {
const msg = chunks.join();
let payload;
if (res.statusCode === 200) {
try {
payload = JSON.parse(msg);
}
catch (error) {
log.error(`Error parsing JSON: ${msg}`);
}
}
else {
log.debug(`Received non-200 HTTP response ${res.statusCode}:\n${msg}`);
}
resolve({
status: res.statusCode,
token: res.headers['x-august-access-token'],
payload: payload,
});
});
});
req.on('error', error => {
log.error(`Error reaching August server: ${error}`);
});
req.write(data);
req.end();
});
}
async function augustLogin(sessionOptions, log) {
const { apiKey, uuid, idType, password, identifier } = sessionOptions;
const data = new util_1.TextEncoder().encode(JSON.stringify({
identifier: `${idType}:${identifier}`,
password: password,
installId: uuid,
}));
const requestOptions = getRequestOptions(apiKey, '/session', 'POST');
const res = await makeRequest(requestOptions, data, log);
if (res.status !== 200 || !res.payload['userId']) {
throw new Error(`Invalid user credentials: ${res.status}`);
}
else {
return {
apiKey: apiKey,
idType: idType,
identifier: identifier,
token: res.token,
};
}
}
async function augustGetMe(session, log) {
const options = getRequestOptions(session.apiKey, '/users/me', 'GET', session);
return makeRequest(options, new Uint8Array(), log);
}
async function augustSendCode(session, log) {
const data = new util_1.TextEncoder().encode(JSON.stringify({
value: session.identifier,
}));
const options = getRequestOptions(session.apiKey, `/validation/${session.idType}`, 'POST', session);
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 = getRequestOptions(session.apiKey, `/validate/${session.idType}`, 'POST', session);
return makeRequest(options, data, log);
}
async function augustGetHouses(session, log) {
const options = getRequestOptions(session.apiKey, '/users/houses/mine', 'GET', session);
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 = getRequestOptions(session.apiKey, '/users/locks/mine', 'GET', session);
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 augustGetDoorStatus(session, lockId, log) {
var _a;
const options = getRequestOptions(session.apiKey, `/remoteoperate/${lockId}/status`, 'PUT', session);
const results = await makeRequest(options, new Uint8Array(), log);
if (results.status === 200 && results.payload) {
const status = results.payload['doorState'];
const serialNumber = (_a = results.payload['info']) === null || _a === void 0 ? void 0 : _a['serialNumber'];
if (status === 'kAugDoorState_Closed') {
return { doorStatus: AugustDoorStatus.CLOSED, serialNumber: serialNumber };
}
else if (status === 'kAugDoorState_Open') {
return { doorStatus: AugustDoorStatus.OPEN, serialNumber: serialNumber };
}
else if (status === 'kAugDoorState_Init') {
return { doorStatus: AugustDoorStatus.UNKNOWN, serialNumber: serialNumber };
}
else if (!status) {
log.info(`Door status for lock ${lockId} unknown. Exclude this device in the config if DoorSense isn't enabled.`);
return { doorStatus: AugustDoorStatus.UNKNOWN, serialNumber: serialNumber };
}
else {
throw new Error(`Unknown door status: ${status}`);
}
}
else {
return { doorStatus: AugustDoorStatus.UNKNOWN };
}
}
exports.augustGetDoorStatus = augustGetDoorStatus;
async function augustGetSerialNumber(session, lockId, log) {
const options = getRequestOptions(session.apiKey, `/remoteoperate/${lockId}/status`, 'PUT', session);
const results = await makeRequest(options, new Uint8Array(), log);
if (results.status === 200 && results.payload) {
return results.payload['serialNumber'];
}
else {
return '';
}
}
exports.augustGetSerialNumber = augustGetSerialNumber;
//# sourceMappingURL=august.js.map