eufy-security-client
Version:
Client to comunicate with Eufy-Security devices
1,034 lines • 77.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HTTPApi = void 0;
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
const i18n_iso_countries_1 = require("i18n-iso-countries");
const i18n_iso_languages_1 = require("@cospired/i18n-iso-languages");
const crypto_1 = require("crypto");
const schedule = __importStar(require("node-schedule"));
const types_1 = require("./types");
const parameter_1 = require("./parameter");
const utils_1 = require("./utils");
const error_1 = require("./../error");
const utils_2 = require("./../utils");
const error_2 = require("./error");
const utils_3 = require("../p2p/utils");
const logging_1 = require("../logging");
class HTTPApi extends tiny_typed_emitter_1.TypedEmitter {
static apiDomainBase = "https://extend.eufylife.com";
SERVER_PUBLIC_KEY = "04c5c00c4f8d1197cc7c3167c52bf7acb054d722f0ef08dcd7e0883236e0d72a3868d9750cb47fa4619248f3d83f0f662671dadc6e2d31c2f41db0161651c7c076";
apiBase;
username;
password;
ecdh = (0, crypto_1.createECDH)("prime256v1");
token = null;
tokenExpiration = null;
renewAuthTokenJob;
connected = false;
requestEufyCloud;
throttle;
devices = {};
hubs = {};
houses = {};
persistentData = {
user_id: "",
email: "",
nick_name: "",
device_public_keys: {},
clientPrivateKey: "",
serverPublicKey: this.SERVER_PUBLIC_KEY
};
headers = {
"User-Agent": undefined,
App_version: "v4.6.0_1630",
Os_type: "android",
Os_version: "31",
Phone_model: "ONEPLUS A3003",
Country: "DE",
Language: "en",
Openudid: "5e4621b0152c0d00",
//uid: "",
Net_type: "wifi",
Mnc: "02",
Mcc: "262",
Sn: "75814221ee75",
Model_type: "PHONE",
Timezone: "GMT+01:00",
"Cache-Control": "no-cache",
};
constructor(apiBase, country, username, password, persistentData) {
super();
this.username = username;
this.password = password;
this.apiBase = apiBase;
logging_1.rootHTTPLogger.debug(`Loaded API`, { apieBase: apiBase, country: country, username: username, persistentData: persistentData });
this.headers.timezone = (0, utils_1.getTimezoneGMTString)();
this.headers.country = country.toUpperCase();
if (persistentData) {
this.persistentData = persistentData;
}
if (this.persistentData.clientPrivateKey === undefined || this.persistentData.clientPrivateKey === "") {
this.ecdh.generateKeys();
this.persistentData.clientPrivateKey = this.ecdh.getPrivateKey().toString("hex");
}
else {
try {
this.ecdh.setPrivateKey(Buffer.from(this.persistentData.clientPrivateKey, "hex"));
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.debug(`Invalid client private key, generate new client private key...`, { error: (0, utils_2.getError)(error) });
this.ecdh.generateKeys();
this.persistentData.clientPrivateKey = this.ecdh.getPrivateKey().toString("hex");
}
}
if (this.persistentData.serverPublicKey === undefined || this.persistentData.serverPublicKey === "") {
this.persistentData.serverPublicKey = this.SERVER_PUBLIC_KEY;
}
else {
try {
this.ecdh.computeSecret(Buffer.from(this.persistentData.serverPublicKey, "hex"));
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.debug(`Invalid server public key, fallback to default server public key...`, { error: (0, utils_2.getError)(error) });
this.persistentData.serverPublicKey = this.SERVER_PUBLIC_KEY;
}
}
}
static async getApiBaseFromCloud(country) {
const { default: got } = await import("got");
const response = await got(`domain/${country}`, {
prefixUrl: this.apiDomainBase,
method: "GET",
responseType: "json",
retry: {
limit: 1,
methods: ["GET"]
}
});
const result = response.body;
if (result.code == types_1.ResponseErrorCode.CODE_WHATEVER_ERROR) {
return `https://${result.data.domain}`;
}
throw new error_2.ApiBaseLoadError("Error identifying API base from cloud", { context: { code: result.code, message: result.msg } });
}
async loadLibraries() {
const { default: pThrottle } = await import("p-throttle");
const { default: got } = await import("got");
this.throttle = pThrottle({
limit: 5,
interval: 1000,
});
this.requestEufyCloud = got.extend({
prefixUrl: this.apiBase,
headers: this.headers,
responseType: "json",
//throwHttpErrors: false,
retry: {
limit: 3,
methods: ["GET", "POST"],
statusCodes: [
404,
408,
413,
423,
429,
500,
502,
503,
504,
521,
522,
524
],
calculateDelay: ({ computedValue }) => {
return computedValue * 3;
}
},
hooks: {
afterResponse: [
async (response, retryWithMergedOptions) => {
// Unauthorized
if (response.statusCode === 401) {
const oldToken = this.token;
logging_1.rootHTTPLogger.debug("Invalidate token an get a new one...", { requestUrl: response.requestUrl, statusCode: response.statusCode, statusMessage: response.statusMessage });
this.invalidateToken();
await this.login({ force: true });
if (oldToken !== this.token && this.token) {
// Refresh the access token
const updatedOptions = {
headers: {
"X-Auth-Token": this.token
}
};
// Update the defaults
this.requestEufyCloud.defaults.options.merge(updatedOptions);
// Make a new retry
return retryWithMergedOptions(updatedOptions);
}
}
// No changes otherwise
return response;
}
],
beforeRetry: [
(error) => {
// This will be called on `retryWithMergedOptions(...)`
const statusCode = error.response?.statusCode || 0;
const { method, url, prefixUrl } = error.options;
const shortUrl = (0, utils_2.getShortUrl)(typeof url === "string" ? new URL(url) : url === undefined ? new URL("") : url, typeof prefixUrl === "string" ? prefixUrl : prefixUrl.toString());
const body = error.response?.body ? error.response?.body : error.message;
logging_1.rootHTTPLogger.debug(`Retrying [${error.request?.retryCount !== undefined ? error.request?.retryCount + 1 : 1}]: ${error.code} (${error.request?.requestUrl})\n${statusCode} ${method} ${shortUrl}\n${body}`);
// Retrying [1]: ERR_NON_2XX_3XX_RESPONSE
}
],
beforeError: [
error => {
const { response, options } = error;
const statusCode = response?.statusCode || 0;
const { method, url, prefixUrl } = options;
const shortUrl = (0, utils_2.getShortUrl)(typeof url === "string" ? new URL(url) : url === undefined ? new URL("") : url, typeof prefixUrl === "string" ? prefixUrl : prefixUrl.toString());
const body = response?.body ? response.body : error.message;
if (response?.body) {
error.name = "EufyApiError";
error.message = `${statusCode} ${method} ${shortUrl}\n${body}`;
}
return error;
}
],
beforeRequest: [
async (_options) => {
await this.throttle(async () => { return; })();
}
]
},
mutableDefaults: true
});
}
static async initialize(country, username, password, persistentData) {
if ((0, i18n_iso_countries_1.isValid)(country) && country.length === 2) {
const apiBase = await this.getApiBaseFromCloud(country);
const api = new HTTPApi(apiBase, country, username, password, persistentData);
await api.loadLibraries();
return api;
}
throw new error_1.InvalidCountryCodeError("Invalid ISO 3166-1 Alpha-2 country code", { context: { countryCode: country } });
}
clearScheduleRenewAuthToken() {
if (this.renewAuthTokenJob !== undefined) {
this.renewAuthTokenJob.cancel();
}
}
scheduleRenewAuthToken() {
this.clearScheduleRenewAuthToken();
if (this.tokenExpiration !== null) {
const scheduleDate = new Date(this.tokenExpiration.getTime() - (1000 * 60 * 60 * 24));
if (this.renewAuthTokenJob === undefined) {
this.renewAuthTokenJob = schedule.scheduleJob("renewAuthToken", scheduleDate, async () => {
logging_1.rootHTTPLogger.info("Authentication token is soon expiring, fetching a new one...");
await this.login({ force: true });
});
}
else {
this.renewAuthTokenJob.schedule(scheduleDate);
}
}
}
invalidateToken() {
this.connected = false;
this.token = null;
this.requestEufyCloud.defaults.options.merge({
headers: {
"X-Auth-Token": undefined
}
});
this.tokenExpiration = null;
this.persistentData.serverPublicKey = this.SERVER_PUBLIC_KEY;
this.clearScheduleRenewAuthToken();
this.emit("auth token invalidated");
}
setPhoneModel(model) {
this.headers.phone_model = model.toUpperCase();
this.requestEufyCloud.defaults.options.merge({
headers: this.headers
});
}
getPhoneModel() {
return this.headers.phone_model;
}
getCountry() {
return this.headers.country;
}
setLanguage(language) {
if ((0, i18n_iso_languages_1.isValid)(language) && language.length === 2) {
this.headers.language = language;
this.requestEufyCloud.defaults.options.merge({
headers: this.headers
});
}
else
throw new error_1.InvalidLanguageCodeError("Invalid ISO 639 language code", { context: { languageCode: language } });
}
getLanguage() {
return this.headers.language;
}
async login(options) {
options = (0, utils_2.mergeDeep)(options, {
force: false
});
logging_1.rootHTTPLogger.debug("Login and get an access token", { token: this.token, tokenExpiration: this.tokenExpiration, options: options });
if (!this.token || (this.tokenExpiration && (new Date()).getTime() >= this.tokenExpiration.getTime()) || options.verifyCode || options.captcha || options.force) {
try {
const data = {
ab: this.headers.country,
client_secret_info: {
public_key: this.ecdh.getPublicKey("hex")
},
enc: 0,
email: this.username,
password: (0, utils_1.encryptAPIData)(this.password, this.ecdh.computeSecret(Buffer.from(this.SERVER_PUBLIC_KEY, "hex"))),
time_zone: new Date().getTimezoneOffset() !== 0 ? -new Date().getTimezoneOffset() * 60 * 1000 : 0,
transaction: `${new Date().getTime()}`
};
if (options.verifyCode) {
data.verify_code = options.verifyCode;
}
else if (options.captcha) {
data.captcha_id = options.captcha.captchaId;
data.answer = options.captcha.captchaCode;
}
const response = await this.request({
method: "post",
endpoint: "v2/passport/login_sec",
data: data
});
if (response.status == 200) {
const result = response.data;
if (result.data !== undefined) {
if (result.code == types_1.ResponseErrorCode.CODE_WHATEVER_ERROR) {
const dataresult = result.data;
if (dataresult.server_secret_info?.public_key)
this.persistentData.serverPublicKey = dataresult.server_secret_info.public_key;
this.persistentData.user_id = dataresult.user_id;
this.persistentData.email = this.decryptAPIData(dataresult.email, false);
this.persistentData.nick_name = dataresult.nick_name;
this.setToken(dataresult.auth_token);
this.tokenExpiration = new Date(dataresult.token_expires_at * 1000);
this.headers = {
...this.headers,
gtoken: (0, utils_2.md5)(dataresult.user_id)
};
logging_1.rootHTTPLogger.debug("Login - Token data", { token: this.token, tokenExpiration: this.tokenExpiration, serverPublicKey: this.persistentData.serverPublicKey });
if (!this.connected) {
this.connected = true;
this.emit("connect");
}
this.scheduleRenewAuthToken();
}
else if (result.code == types_1.ResponseErrorCode.CODE_NEED_VERIFY_CODE) {
logging_1.rootHTTPLogger.debug(`Login - Send verification code...`);
const dataresult = result.data;
this.setToken(dataresult.auth_token);
this.tokenExpiration = new Date(dataresult.token_expires_at * 1000);
logging_1.rootHTTPLogger.debug("Token data", { token: this.token, tokenExpiration: this.tokenExpiration });
await this.sendVerifyCode(types_1.VerfyCodeTypes.TYPE_EMAIL);
logging_1.rootHTTPLogger.info("Please send required verification code to proceed with authentication");
this.emit("tfa request");
}
else if (result.code == types_1.ResponseErrorCode.LOGIN_NEED_CAPTCHA || result.code == types_1.ResponseErrorCode.LOGIN_CAPTCHA_ERROR) {
const dataresult = result.data;
logging_1.rootHTTPLogger.debug("Login - Captcha verification received", { captchaId: dataresult.captcha_id, item: dataresult.item });
logging_1.rootHTTPLogger.info("Please send requested captcha to proceed with authentication");
this.emit("captcha request", dataresult.captcha_id, dataresult.item);
}
else {
logging_1.rootHTTPLogger.error("Login - Response code not ok", { code: result.code, msg: result.msg, data: response.data });
this.emit("connection error", new error_2.ApiResponseCodeError("API response code not ok", { context: { code: result.code, message: result.msg } }));
}
}
else {
logging_1.rootHTTPLogger.error("Login - Response data is missing", { code: result.code, msg: result.msg, data: result.data });
this.emit("connection error", new error_2.ApiInvalidResponseError("API response data is missing", { context: { code: result.code, message: result.msg, data: result.data } }));
}
}
else {
logging_1.rootHTTPLogger.error("Login - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data });
this.emit("connection error", new error_2.ApiHTTPResponseCodeError("API HTTP response code not ok", { context: { status: response.status, statusText: response.statusText } }));
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Login - Generic Error:", { error: (0, utils_2.getError)(error) });
this.emit("connection error", new error_2.ApiGenericError("Generic API error", { cause: error }));
}
}
else if (!this.connected) {
try {
const profile = await this.getPassportProfile();
if (profile !== null) {
if (!this.connected) {
this.connected = true;
this.emit("connect");
this.scheduleRenewAuthToken();
}
}
else {
this.emit("connection error", new error_2.ApiInvalidResponseError(`Invalid passport profile response`));
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Login - getPassportProfile Error", { error: (0, utils_2.getError)(error) });
this.emit("connection error", new error_2.ApiGenericError("API get passport profile error", { cause: error }));
}
}
}
async sendVerifyCode(type) {
try {
if (!type)
type = types_1.VerfyCodeTypes.TYPE_EMAIL;
const response = await this.request({
method: "post",
endpoint: "v1/sms/send/verify_code",
data: {
message_type: type,
transaction: `${new Date().getTime()}`
}
});
if (response.status == 200) {
const result = response.data;
if (result.code == types_1.ResponseErrorCode.CODE_WHATEVER_ERROR) {
logging_1.rootHTTPLogger.info(`Requested verification code for 2FA`);
return true;
}
else {
logging_1.rootHTTPLogger.error("Send verify code - Response code not ok", { code: result.code, msg: result.msg, data: response.data });
}
}
else {
logging_1.rootHTTPLogger.error("Send verify code - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Send verify code - Generic Error", { error: (0, utils_2.getError)(error) });
}
return false;
}
async listTrustDevice() {
if (this.connected) {
try {
const response = await this.request({
method: "get",
endpoint: "v1/app/trust_device/list"
});
if (response.status == 200) {
const result = response.data;
if (result.code == types_1.ResponseErrorCode.CODE_WHATEVER_ERROR) {
if (result.data && result.data.list) {
return result.data.list;
}
}
else {
logging_1.rootHTTPLogger.error("List trust device - Response code not ok", { code: result.code, msg: result.msg, data: response.data });
}
}
else {
logging_1.rootHTTPLogger.error("List trust device - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("List trust device - Generic Error", { error: (0, utils_2.getError)(error) });
}
}
return [];
}
async addTrustDevice(verifyCode) {
if (this.connected) {
try {
const response = await this.request({
method: "post",
endpoint: "v1/app/trust_device/add",
data: {
verify_code: verifyCode,
transaction: `${new Date().getTime()}`
}
});
logging_1.rootHTTPLogger.debug("Add trust device - Response trust device", { verifyCode: verifyCode, data: response.data });
if (response.status == 200) {
const result = response.data;
if (result.code == types_1.ResponseErrorCode.CODE_WHATEVER_ERROR) {
logging_1.rootHTTPLogger.info(`2FA authentication successfully done. Device trusted.`);
const trusted_devices = await this.listTrustDevice();
trusted_devices.forEach((trusted_device) => {
if (trusted_device.is_current_device === 1) {
logging_1.rootHTTPLogger.debug("Add trust device - This device is trusted. Token expiration extended:", { trustDevice: { phoneModel: trusted_device.phone_model, openUdid: trusted_device.open_udid }, tokenExpiration: this.tokenExpiration });
}
});
return true;
}
else {
logging_1.rootHTTPLogger.error("Add trust device - Response code not ok", { code: result.code, msg: result.msg, verifyCode: verifyCode, data: response.data });
}
}
else {
logging_1.rootHTTPLogger.error("Add trust device - Status return code not 200", { status: response.status, statusText: response.statusText, verifyCode: verifyCode, data: response.data });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Add trust device - Generic Error", { error: (0, utils_2.getError)(error) });
}
}
return false;
}
async getStationList() {
if (this.connected) {
try {
const response = await this.request({
method: "post",
endpoint: "v2/house/station_list",
data: {
device_sn: "",
num: 1000,
orderby: "",
page: 0,
station_sn: "",
time_zone: new Date().getTimezoneOffset() !== 0 ? -new Date().getTimezoneOffset() * 60 * 1000 : 0,
transaction: `${new Date().getTime()}`
}
});
if (response.status == 200) {
const result = response.data;
if (result.code == 0) {
if (result.data) {
const stationList = this.decryptAPIData(result.data);
logging_1.rootHTTPLogger.debug("Decrypted station list data", stationList);
return stationList;
}
}
else {
logging_1.rootHTTPLogger.error("Station list - Response code not ok", { code: result.code, msg: result.msg, data: response.data });
}
}
else {
logging_1.rootHTTPLogger.error("Station list - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Station list - Generic Error", { error: (0, utils_2.getError)(error) });
}
}
return [];
}
async getDeviceList() {
if (this.connected) {
try {
const response = await this.request({
method: "post",
endpoint: "v2/house/device_list",
data: {
device_sn: "",
num: 1000,
orderby: "",
page: 0,
station_sn: "",
time_zone: new Date().getTimezoneOffset() !== 0 ? -new Date().getTimezoneOffset() * 60 * 1000 : 0,
transaction: `${new Date().getTime()}`
}
});
if (response.status == 200) {
const result = response.data;
if (result.code == 0) {
if (result.data) {
const deviceList = this.decryptAPIData(result.data);
logging_1.rootHTTPLogger.debug("Decrypted device list data", deviceList);
return deviceList;
}
}
else {
logging_1.rootHTTPLogger.error("Device list - Response code not ok", { code: result.code, msg: result.msg, data: response.data });
}
}
else {
logging_1.rootHTTPLogger.error("Device list - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Device list - Generic Error", { error: (0, utils_2.getError)(error) });
}
}
return [];
}
async refreshHouseData() {
//Get Houses
const houses = await this.getHouseList();
if (houses && houses.length > 0) {
houses.forEach(element => {
this.houses[element.house_id] = element;
});
}
else {
logging_1.rootHTTPLogger.info("No houses found.");
}
this.emit("houses", this.houses);
}
async refreshStationData() {
//Get Stations
const stations = await this.getStationList();
if (stations && stations.length > 0) {
stations.forEach(element => {
this.hubs[element.station_sn] = element;
});
}
else {
logging_1.rootHTTPLogger.info("No stations found.");
}
this.emit("hubs", this.hubs);
}
async refreshDeviceData() {
//Get Devices
const devices = await this.getDeviceList();
if (devices && devices.length > 0) {
devices.forEach(element => {
this.devices[element.device_sn] = element;
});
}
else {
logging_1.rootHTTPLogger.info("No devices found.");
}
this.emit("devices", this.devices);
}
async refreshAllData() {
//Get the latest info
//Get Houses
await this.refreshHouseData();
//Get Stations
await this.refreshStationData();
//Get Devices
await this.refreshDeviceData();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
async request(request, withoutUrlPrefix = false) {
logging_1.rootHTTPLogger.debug("Api request", { method: request.method, endpoint: request.endpoint, responseType: request.responseType, token: this.token, data: request.data });
try {
let options;
switch (request.responseType) {
case undefined:
case "json":
options = {
method: request.method,
json: request.data,
responseType: "json",
};
break;
case "text":
options = {
method: request.method,
json: request.data,
responseType: request.responseType,
};
break;
case "buffer":
options = {
method: request.method,
json: request.data,
responseType: request.responseType,
};
break;
}
if (withoutUrlPrefix)
options.prefixUrl = "";
const internalResponse = await this.requestEufyCloud(request.endpoint, options);
const response = {
status: internalResponse.statusCode,
statusText: internalResponse.statusMessage ? internalResponse.statusMessage : "",
headers: internalResponse.headers,
data: internalResponse.body,
};
logging_1.rootHTTPLogger.debug("Api request - Response", { token: this.token, request: request, response: response.data });
return response;
}
catch (err) {
const error = (0, error_1.ensureError)(err);
if (error instanceof (await import("got")).HTTPError) {
if (error.response.statusCode === 401) {
this.invalidateToken();
logging_1.rootHTTPLogger.error("Status return code 401, invalidate token", { status: error.response.statusCode, statusText: error.response.statusMessage });
this.emit("close");
}
}
throw new error_2.ApiRequestError("API request error", { cause: error, context: { method: request.method, endpoint: request.endpoint, responseType: request.responseType, token: this.token, data: request.data } });
}
}
async checkPushToken() {
//Check push notification token
if (this.connected) {
try {
const response = await this.request({
method: "post",
endpoint: "v1/app/review/app_push_check",
data: {
app_type: "eufySecurity",
transaction: `${new Date().getTime()}`
}
});
if (response.status == 200) {
const result = response.data;
if (result.code == 0) {
logging_1.rootHTTPLogger.debug(`Check push token - Push token OK`);
return true;
}
else {
logging_1.rootHTTPLogger.error("Check push token - Response code not ok", { code: result.code, msg: result.msg, data: response.data });
}
}
else {
logging_1.rootHTTPLogger.error("Check push token - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Check push token - Generic Error", { error: (0, utils_2.getError)(error) });
}
}
return false;
}
async registerPushToken(token) {
//Register push notification token
if (this.connected) {
try {
const response = await this.request({
method: "post",
endpoint: "v1/apppush/register_push_token",
data: {
is_notification_enable: true,
token: token,
transaction: `${new Date().getTime().toString()}`
}
});
if (response.status == 200) {
const result = response.data;
if (result.code == 0) {
logging_1.rootHTTPLogger.debug(`Register push token - Push token registered successfully`);
return true;
}
else {
logging_1.rootHTTPLogger.error("Register push token - Response code not ok", { code: result.code, msg: result.msg, data: response.data, token: token });
}
}
else {
logging_1.rootHTTPLogger.error("Register push token - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data, token: token });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Register push token - Generic Error", { error: (0, utils_2.getError)(error), token: token });
}
}
return false;
}
async setParameters(stationSN, deviceSN, params) {
if (this.connected) {
const tmp_params = [];
params.forEach(param => {
tmp_params.push({ param_type: param.paramType, param_value: parameter_1.ParameterHelper.writeValue(param.paramType, param.paramValue) });
});
try {
const response = await this.request({
method: "post",
endpoint: "v1/app/upload_devs_params",
data: {
device_sn: deviceSN,
station_sn: stationSN,
params: tmp_params,
transaction: `${new Date().getTime().toString()}`
}
});
logging_1.rootHTTPLogger.debug("Set parameter - Response:", { stationSN: stationSN, deviceSN: deviceSN, params: tmp_params, response: response.data });
if (response.status == 200) {
const result = response.data;
if (result.code == 0) {
const dataresult = result.data;
logging_1.rootHTTPLogger.debug("Set parameter - New parameters set", { params: tmp_params, response: dataresult });
return true;
}
else {
logging_1.rootHTTPLogger.error("Set parameter - Response code not ok", { code: result.code, msg: result.msg, data: response.data, stationSN: stationSN, deviceSN: deviceSN, params: params });
}
}
else {
logging_1.rootHTTPLogger.error("Set parameter - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data, stationSN: stationSN, deviceSN: deviceSN, params: params });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Set parameter - Generic Error", { error: (0, utils_2.getError)(error), stationSN: stationSN, deviceSN: deviceSN, params: params });
}
}
return false;
}
async getCiphers(/*stationSN: string, */ cipherIDs, userID) {
if (this.connected) {
try {
const response = await this.request({
method: "post",
endpoint: "v2/app/cipher/get_ciphers",
data: {
cipher_ids: cipherIDs,
user_id: userID,
//sn: stationSN
transaction: `${new Date().getTime().toString()}`
}
});
if (response.status == 200) {
const result = response.data;
if (result.code == types_1.ResponseErrorCode.CODE_WHATEVER_ERROR) {
if (result.data) {
const ciphers = {};
const decrypted = this.decryptAPIData(result.data);
logging_1.rootHTTPLogger.debug("Get ciphers - Decrypted ciphers data", { ciphers: decrypted });
if (Array.isArray(decrypted)) {
decrypted.forEach((cipher) => {
ciphers[cipher.cipher_id] = cipher;
});
}
return ciphers;
}
}
else {
logging_1.rootHTTPLogger.error("Get ciphers - Response code not ok", { code: result.code, msg: result.msg, data: response.data, cipherIDs: cipherIDs, userID: userID });
}
}
else {
logging_1.rootHTTPLogger.error("Get ciphers - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data, cipherIDs: cipherIDs, userID: userID });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Get ciphers - Generic Error", { error: (0, utils_2.getError)(error), cipherIDs: cipherIDs, userID: userID });
}
}
return {};
}
async getVoices(deviceSN) {
if (this.connected) {
try {
const response = await this.request({
method: "get",
endpoint: `v1/voice/response/lists/${deviceSN}`
});
if (response.status == 200) {
const result = response.data;
if (result.code == types_1.ResponseErrorCode.CODE_WHATEVER_ERROR) {
if (result.data) {
const voices = {};
result.data.forEach((voice) => {
voices[voice.voice_id] = voice;
});
return voices;
}
}
else {
logging_1.rootHTTPLogger.error("Get Voices - Response code not ok", { code: result.code, msg: result.msg, data: response.data, deviceSN: deviceSN });
}
}
else {
logging_1.rootHTTPLogger.error("Get Voices - Status return code not 200", { status: response.status, statusText: response.statusText, data: response.data, deviceSN: deviceSN });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error("Get Voices - Generic Error", { error: (0, utils_2.getError)(error), deviceSN: deviceSN });
}
}
return {};
}
async getCipher(/*stationSN: string, */ cipherID, userID) {
return (await this.getCiphers(/*stationSN, */ [cipherID], userID))[cipherID];
}
getDevices() {
return this.devices;
}
getHubs() {
return this.hubs;
}
getToken() {
return this.token;
}
getTokenExpiration() {
return this.tokenExpiration;
}
setToken(token) {
this.token = token;
this.requestEufyCloud.defaults.options.merge({
headers: {
"X-Auth-Token": token
}
});
}
setTokenExpiration(tokenExpiration) {
this.tokenExpiration = tokenExpiration;
}
getAPIBase() {
return typeof this.requestEufyCloud.defaults.options.prefixUrl === "string" ? this.requestEufyCloud.defaults.options.prefixUrl : this.requestEufyCloud.defaults.options.prefixUrl.toString();
}
setOpenUDID(openudid) {
this.headers.openudid = openudid;
this.requestEufyCloud.defaults.options.merge({
headers: this.headers
});
}
setSerialNumber(serialnumber) {
this.headers.sn = serialnumber;
this.requestEufyCloud.defaults.options.merge({
headers: this.headers
});
}
async _getEvents(functionName, endpoint, startTime, endTime, filter, maxResults) {
const records = [];
if (this.connected) {
try {
if (filter === undefined)
filter = { deviceSN: "", stationSN: "", storageType: types_1.StorageType.NONE };
if (maxResults === undefined)
maxResults = 1000;
const response = await this.request({
method: "post",
endpoint: endpoint,
data: {
device_sn: filter.deviceSN !== undefined ? filter.deviceSN : "",
end_time: Math.trunc(endTime.getTime() / 1000),
exclude_guest: false,
house_id: "HOUSEID_ALL_DEVICE",
id: 0,
id_type: 1,
is_favorite: false,
num: maxResults,
pullup: true,
shared: true,
start_time: Math.trunc(startTime.getTime() / 1000),
station_sn: filter.stationSN !== undefined ? filter.stationSN : "",
storage: filter.storageType !== undefined ? filter.storageType : types_1.StorageType.NONE,
transaction: `${new Date().getTime().toString()}`
}
});
logging_1.rootHTTPLogger.debug(`${functionName} - Response:`, response.data);
if (response.status == 200) {
const result = response.data;
if (result.code == 0) {
if (result.data) {
const dataresult = this.decryptAPIData(result.data);
logging_1.rootHTTPLogger.debug(`${functionName} - Decrypted data:`, dataresult);
if (dataresult) {
dataresult.forEach(record => {
logging_1.rootHTTPLogger.debug(`${functionName} - Record:`, record);
records.push(record);
});
}
}
else {
logging_1.rootHTTPLogger.error(`${functionName} - Response data is missing`, { code: result.code, msg: result.msg, data: response.data, endpoint: endpoint, startTime: startTime, endTime: endTime, filter: filter, maxResults: maxResults });
}
}
else {
logging_1.rootHTTPLogger.error(`${functionName} - Response code not ok`, { code: result.code, msg: result.msg, data: response.data, endpoint: endpoint, startTime: startTime, endTime: endTime, filter: filter, maxResults: maxResults });
}
}
else {
logging_1.rootHTTPLogger.error(`${functionName} - Status return code not 200`, { status: response.status, statusText: response.statusText, data: response.data, endpoint: endpoint, startTime: startTime, endTime: endTime, filter: filter, maxResults: maxResults });
}
}
catch (err) {
const error = (0, error_1.ensureError)(err);
logging_1.rootHTTPLogger.error(`${functionName} - Generic Error`, { error: (0, utils_2.getError)(error), endpoint: endpoint, startTime: startTime, endTime: endTime, filter: filter, maxResults: maxResults });
}
}
return records;
}
async getVideoEvents(startTime, endTime, filter, maxResults) {
return this._getEvents("getVideoEvents", "v2/event/app/get_all_video_record", startTime, endTime, filter, maxResults);
}
async getAlarmEvents(startTime, endTime, filter, maxResults) {
return this._getEvents("getAlarmEvents", "v2/event/app/get_all_alarm_record", startTime, endTime, filter, maxResults);
}
async getHistoryEvents(startTime, endTime, filter, maxResults) {
return this._getEvents("getHistoryEvents", "v2/event/app/get_all_history_record", startTime, endTime, filter, maxResults);
}
async getAllVideoEvents(filter, maxResults) {
const fifteenYearsInMilliseconds = 15 * 365 * 24 * 60 * 60 * 1000;
return this.getVideoEvents(new Date(new Date().getTime() - fifteenYearsInMilliseconds), new Date(), filter, maxResults);
}
async getAllAlarmEvents(filter, maxResults) {
const fifteenYearsInMilliseconds = 15 * 365 * 24 * 60 * 60 * 1000;
return this.getAlarmEvents(new Date(new Date().getTime() - fifteenYearsInMilliseconds), new Date(), filter, maxResults);
}
async getAllHistoryEvents(filter, maxResults) {
const fifteenYearsInMilliseconds = 15 * 365 * 24 * 60 * 60 * 1000;
return this.getHistoryEvents(new Date(new Date().getTime() - fifteenYearsInMilliseconds), new Date(), filter, maxResults);
}
isConnected() {
return this.connected;
}
async getInvites() {
if (this.connected) {
try {
const response = await this.request({
method: "post",
endpoint: "v2/family/get_invites",
data: {
num: 100,
orderby: "",
own: false,
page: 0,
transaction: `${new Date().getTime().toString()}`
}
});
if (response.status == 200) {
const result = response.data;
if (result.code == types_1.ResponseErrorCode.CODE_WHATEVER_ERROR) {
if (result.data) {
const invites = {};
const decrypted = this.decryptAPIData(result.data);
logging_1.rootHTTPLogger.debug("Get invites - Decrypted invites data", { invites: decrypted });
if (Array.isArray(decrypted)) {
decrypted.forEach((invite) => {
invites[invite.invite_id] = invite;
let data = (0, utils_2.parseJSON)(invites[invite.invite_id].devices, logging_1.rootHTTPLogger);
if (data === undefined)
data = [];
invites[invite.invite_id].devices = data;
});
}
return invites;
}
}
else {
logging_1.rootHTTPLogger.error("Get invites - Response code not ok", { code: result.code, msg: result.msg, data: response.d