eufy-security-client-fork
Version:
Client to comunicate with Eufy-Security devices
145 lines • 6.42 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MQTTService = void 0;
const mqtt = require("mqtt");
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
const ts_log_1 = require("ts-log");
const fse = require("fs-extra");
const path = require("path");
const protobuf_typescript_1 = require("protobuf-typescript");
class MQTTService extends tiny_typed_emitter_1.TypedEmitter {
constructor(log = ts_log_1.dummyLogger) {
super();
this.CLIENT_ID_FORMAT = "android_EufySecurity_<user_id>_<android_id>";
this.USERNAME_FORMAT = "eufy_<user_id>";
this.SUBSCRIBE_NOTICE_FORMAT = "/phone/<user_id>/notice";
this.SUBSCRIBE_LOCK_FORMAT = "/phone/smart_lock/<device_sn>/push_message";
this.SUBSCRIBE_DOORBELL_FORMAT = "/phone/doorbell/<device_sn>/push_message";
this.connected = false;
this.client = null;
this.connecting = false;
this.subscribeLocks = [];
this.log = log;
this.deviceSmartLockMessageModel = MQTTService.proto.lookupType("DeviceSmartLockMessage");
}
static async init(log = ts_log_1.dummyLogger) {
this.proto = await (0, protobuf_typescript_1.load)(path.join(__dirname, "./proto/lock.proto"));
return new MQTTService(log);
}
parseSmartLockMessage(data) {
const message = this.deviceSmartLockMessageModel.decode(data);
const object = this.deviceSmartLockMessageModel.toObject(message, {
longs: String,
enums: String,
bytes: String,
});
return object;
}
getMQTTBrokerUrl(apiBase) {
switch (apiBase) {
case "https://security-app.eufylife.com":
return "mqtts://security-mqtt.eufylife.com";
case "https://security-app-ci.eufylife.com":
return "mqtts://security-mqtt-ci.eufylife.com";
case "https://security-app-qa.eufylife.com":
case "https://security-app-cn-qa.anker-in.com":
return "mqtts://security-mqtt-qa.eufylife.com";
case "https://security-app-eu.eufylife.com":
return "mqtts://security-mqtt-eu.eufylife.com";
case "https://security-app-short-qa.eufylife.com":
return "mqtts://security-mqtt-short-qa.eufylife.com";
default:
return "mqtts://security-mqtt.eufylife.com";
}
}
connect(clientID, androidID, apiBase, email) {
this.clientID = clientID;
this.androidID = androidID;
this.apiBase = apiBase;
this.email = email;
if (!this.connected && !this.connecting && this.clientID && this.androidID && this.apiBase && this.email && this.subscribeLocks.length > 0) {
this.connecting = true;
this.client = mqtt.connect(this.getMQTTBrokerUrl(apiBase), {
keepalive: 60,
clean: true,
reschedulePings: true,
resubscribe: true,
port: 8789,
username: this.USERNAME_FORMAT.replace("<user_id>", clientID),
password: email,
ca: fse.readFileSync(path.join(__dirname, "./mqtt-eufy.crt")),
clientId: this.CLIENT_ID_FORMAT.replace("<user_id>", clientID).replace("<android_id>", androidID)
});
this.client.on("connect", (_connack) => {
this.connected = true;
this.connecting = false;
this.emit("connect");
this.client.subscribe(this.SUBSCRIBE_NOTICE_FORMAT.replace("<user_id>", clientID), { qos: 1 });
if (this.subscribeLocks.length > 0) {
let lock;
while ((lock = this.subscribeLocks.shift()) !== undefined) {
this._subscribeLock(lock);
}
}
});
this.client.on("close", () => {
this.connected = false;
this.emit("close");
});
this.client.on("error", (error) => {
var _a;
this.connecting = false;
this.log.error("MQTT Error", error);
if (error.code === 1 || error.code === 2 || error.code === 4 || error.code === 5)
(_a = this.client) === null || _a === void 0 ? void 0 : _a.end();
});
this.client.on("message", (topic, message, _packet) => {
if (topic.includes("smart_lock")) {
const parsedMessage = this.parseSmartLockMessage(message);
this.log.debug("Received a smart lock message over MQTT", parsedMessage);
this.emit("lock message", parsedMessage);
}
else {
this.log.debug("MQTT message received", topic, message.toString("hex"));
}
});
}
}
_subscribeLock(deviceSN) {
var _a;
(_a = this.client) === null || _a === void 0 ? void 0 : _a.subscribe(this.SUBSCRIBE_LOCK_FORMAT.replace("<device_sn>", deviceSN), { qos: 1 }, (error, granted) => {
if (error) {
this.log.error(`Subscribe error for lock ${deviceSN}`, error);
}
if (granted) {
this.log.info(`Successfully registered to MQTT notifications for lock ${deviceSN}`);
}
});
}
subscribeLock(deviceSN) {
if (this.connected) {
this._subscribeLock(deviceSN);
}
else {
if (!this.subscribeLocks.includes(deviceSN)) {
this.subscribeLocks.push(deviceSN);
}
if (this.clientID && this.androidID && this.apiBase && this.email)
this.connect(this.clientID, this.androidID, this.apiBase, this.email);
}
}
isConnected() {
return this.connected;
}
close() {
var _a;
if (this.connected) {
(_a = this.client) === null || _a === void 0 ? void 0 : _a.end(true);
this.connected = false;
this.connecting = false;
}
}
}
exports.MQTTService = MQTTService;
MQTTService.proto = null;
//# sourceMappingURL=service.js.map