iobroker.eusec 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +569 -0
- package/adapter-settings.js +2423 -0
- package/admin/admin.d.ts +93 -0
- package/admin/euSec.png +0 -0
- package/admin/i18n/de/translations.json +18 -0
- package/admin/i18n/en/translations.json +18 -0
- package/admin/i18n/es/translations.json +18 -0
- package/admin/i18n/fr/translations.json +18 -0
- package/admin/i18n/it/translations.json +18 -0
- package/admin/i18n/nl/translations.json +18 -0
- package/admin/i18n/pl/translations.json +18 -0
- package/admin/i18n/pt/translations.json +18 -0
- package/admin/i18n/ru/translations.json +18 -0
- package/admin/i18n/zh-cn/translations.json +18 -0
- package/admin/index_m.html +156 -0
- package/admin/jsonConfig.json +134 -0
- package/admin/style.css +31 -0
- package/admin/words.js +221 -0
- package/build/lib/interfaces.js +3 -0
- package/build/lib/interfaces.js.map +1 -0
- package/build/lib/log.js +53 -0
- package/build/lib/log.js.map +1 -0
- package/build/lib/tools.js +99 -0
- package/build/lib/tools.js.map +1 -0
- package/build/lib/types.js +127 -0
- package/build/lib/types.js.map +1 -0
- package/build/lib/utils.js +451 -0
- package/build/lib/utils.js.map +1 -0
- package/build/lib/video.js +224 -0
- package/build/lib/video.js.map +1 -0
- package/build/lib/web.js +86 -0
- package/build/lib/web.js.map +1 -0
- package/build/main.js +1358 -0
- package/build/main.js.map +1 -0
- package/io-package.json +216 -0
- package/package.json +84 -0
package/build/main.js
ADDED
|
@@ -0,0 +1,1358 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Created with @iobroker/create-adapter v1.28.0
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
8
|
+
}) : (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
o[k2] = m[k];
|
|
11
|
+
}));
|
|
12
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
13
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
14
|
+
}) : function(o, v) {
|
|
15
|
+
o["default"] = v;
|
|
16
|
+
});
|
|
17
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
18
|
+
if (mod && mod.__esModule) return mod;
|
|
19
|
+
var result = {};
|
|
20
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
21
|
+
__setModuleDefault(result, mod);
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
24
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
25
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.euSec = void 0;
|
|
29
|
+
const utils = __importStar(require("@iobroker/adapter-core"));
|
|
30
|
+
const path = __importStar(require("path"));
|
|
31
|
+
const eufy_security_client_1 = require("eufy-security-client");
|
|
32
|
+
const i18n_iso_countries_1 = require("i18n-iso-countries");
|
|
33
|
+
const i18n_iso_languages_1 = require("@cospired/i18n-iso-languages");
|
|
34
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
35
|
+
const util_1 = __importDefault(require("util"));
|
|
36
|
+
const types_1 = require("./lib/types");
|
|
37
|
+
const utils_1 = require("./lib/utils");
|
|
38
|
+
const log_1 = require("./lib/log");
|
|
39
|
+
const video_1 = require("./lib/video");
|
|
40
|
+
class euSec extends utils.Adapter {
|
|
41
|
+
constructor(options = {}) {
|
|
42
|
+
super({
|
|
43
|
+
...options,
|
|
44
|
+
name: "eusec",
|
|
45
|
+
});
|
|
46
|
+
this.downloadEvent = {};
|
|
47
|
+
this.persistentData = {
|
|
48
|
+
version: ""
|
|
49
|
+
};
|
|
50
|
+
this.rtmpFFmpegPromise = new Map();
|
|
51
|
+
this.captchaId = null;
|
|
52
|
+
this.verify_code = false;
|
|
53
|
+
const data_dir = utils.getAbsoluteInstanceDataDir(this);
|
|
54
|
+
this.persistentFile = path.join(data_dir, "adapter.json");
|
|
55
|
+
if (!fs_extra_1.default.existsSync(data_dir))
|
|
56
|
+
fs_extra_1.default.mkdirSync(data_dir);
|
|
57
|
+
this.on("ready", this.onReady.bind(this));
|
|
58
|
+
this.on("stateChange", this.onStateChange.bind(this));
|
|
59
|
+
// this.on("objectChange", this.onObjectChange.bind(this));
|
|
60
|
+
// this.on("message", this.onMessage.bind(this));
|
|
61
|
+
this.on("unload", this.onUnload.bind(this));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Is called when databases are connected and adapter received configuration.
|
|
65
|
+
*/
|
|
66
|
+
async onReady() {
|
|
67
|
+
this.logger = new log_1.ioBrokerLogger(this.log);
|
|
68
|
+
await this.setObjectNotExistsAsync("verify_code", {
|
|
69
|
+
type: "state",
|
|
70
|
+
common: {
|
|
71
|
+
name: "2FA verification code",
|
|
72
|
+
type: "string",
|
|
73
|
+
role: "state",
|
|
74
|
+
read: true,
|
|
75
|
+
write: true,
|
|
76
|
+
},
|
|
77
|
+
native: {},
|
|
78
|
+
});
|
|
79
|
+
await this.setObjectNotExistsAsync("received_captcha_html", {
|
|
80
|
+
type: "state",
|
|
81
|
+
common: {
|
|
82
|
+
name: "Received captcha image HTML",
|
|
83
|
+
type: "string",
|
|
84
|
+
role: "state",
|
|
85
|
+
read: true,
|
|
86
|
+
write: false,
|
|
87
|
+
},
|
|
88
|
+
native: {},
|
|
89
|
+
});
|
|
90
|
+
await this.setObjectNotExistsAsync("captcha", {
|
|
91
|
+
type: "state",
|
|
92
|
+
common: {
|
|
93
|
+
name: "Enter captcha",
|
|
94
|
+
type: "string",
|
|
95
|
+
role: "state",
|
|
96
|
+
read: true,
|
|
97
|
+
write: true,
|
|
98
|
+
},
|
|
99
|
+
native: {},
|
|
100
|
+
});
|
|
101
|
+
await this.setObjectNotExistsAsync("info", {
|
|
102
|
+
type: "channel",
|
|
103
|
+
common: {
|
|
104
|
+
name: "info"
|
|
105
|
+
},
|
|
106
|
+
native: {},
|
|
107
|
+
});
|
|
108
|
+
await this.setObjectNotExistsAsync("info.connection", {
|
|
109
|
+
type: "state",
|
|
110
|
+
common: {
|
|
111
|
+
name: "Global connection",
|
|
112
|
+
type: "boolean",
|
|
113
|
+
role: "indicator.connection",
|
|
114
|
+
read: true,
|
|
115
|
+
write: false,
|
|
116
|
+
},
|
|
117
|
+
native: {},
|
|
118
|
+
});
|
|
119
|
+
await this.setStateAsync("info.connection", { val: false, ack: true });
|
|
120
|
+
await this.setObjectNotExistsAsync("info.push_connection", {
|
|
121
|
+
type: "state",
|
|
122
|
+
common: {
|
|
123
|
+
name: "Push notification connection",
|
|
124
|
+
type: "boolean",
|
|
125
|
+
role: "indicator.connection",
|
|
126
|
+
read: true,
|
|
127
|
+
write: false,
|
|
128
|
+
},
|
|
129
|
+
native: {},
|
|
130
|
+
});
|
|
131
|
+
await this.setStateAsync("info.push_connection", { val: false, ack: true });
|
|
132
|
+
await this.setObjectNotExistsAsync("info.mqtt_connection", {
|
|
133
|
+
type: "state",
|
|
134
|
+
common: {
|
|
135
|
+
name: "MQTT connection",
|
|
136
|
+
type: "boolean",
|
|
137
|
+
role: "indicator.connection",
|
|
138
|
+
read: true,
|
|
139
|
+
write: false,
|
|
140
|
+
},
|
|
141
|
+
native: {},
|
|
142
|
+
});
|
|
143
|
+
await this.setStateAsync("info.mqtt_connection", { val: false, ack: true });
|
|
144
|
+
try {
|
|
145
|
+
const connection = await this.getStatesAsync("*.connection");
|
|
146
|
+
if (connection)
|
|
147
|
+
Object.keys(connection).forEach(async (id) => {
|
|
148
|
+
await this.setStateAsync(id, { val: false, ack: true });
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
this.logger.error("Reset connection states - Error", error);
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const sensorList = [
|
|
156
|
+
eufy_security_client_1.PropertyName.DeviceMotionDetected,
|
|
157
|
+
eufy_security_client_1.PropertyName.DevicePersonDetected,
|
|
158
|
+
eufy_security_client_1.PropertyName.DeviceSoundDetected,
|
|
159
|
+
eufy_security_client_1.PropertyName.DeviceCryingDetected,
|
|
160
|
+
eufy_security_client_1.PropertyName.DevicePetDetected,
|
|
161
|
+
eufy_security_client_1.PropertyName.DeviceRinging
|
|
162
|
+
];
|
|
163
|
+
for (const sensorName of sensorList) {
|
|
164
|
+
const sensors = await this.getStatesAsync(`*.${(0, utils_1.convertCamelCaseToSnakeCase)(sensorName)}`);
|
|
165
|
+
if (sensors)
|
|
166
|
+
Object.keys(sensors).forEach(async (id) => {
|
|
167
|
+
await this.setStateAsync(id, { val: false, ack: true });
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
this.logger.error("Reset sensor states - Error", error);
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
if (fs_extra_1.default.statSync(this.persistentFile).isFile()) {
|
|
176
|
+
const fileContent = fs_extra_1.default.readFileSync(this.persistentFile, "utf8");
|
|
177
|
+
this.persistentData = JSON.parse(fileContent);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
this.logger.debug("No stored data from last exit found.", error);
|
|
182
|
+
}
|
|
183
|
+
this.subscribeStates("verify_code");
|
|
184
|
+
this.subscribeStates("captcha");
|
|
185
|
+
const systemConfig = await this.getForeignObjectAsync("system.config");
|
|
186
|
+
let countryCode = undefined;
|
|
187
|
+
let languageCode = undefined;
|
|
188
|
+
if (systemConfig) {
|
|
189
|
+
countryCode = (0, i18n_iso_countries_1.getAlpha2Code)(systemConfig.common.country, "en");
|
|
190
|
+
if ((0, i18n_iso_languages_1.isValid)(systemConfig.common.language))
|
|
191
|
+
languageCode = systemConfig.common.language;
|
|
192
|
+
}
|
|
193
|
+
// Handling adapter version update
|
|
194
|
+
try {
|
|
195
|
+
const adapter_info = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
|
|
196
|
+
if (adapter_info && adapter_info.common && adapter_info.common.version) {
|
|
197
|
+
if (this.persistentData.version !== adapter_info.common.version) {
|
|
198
|
+
const currentVersion = Number.parseFloat((0, utils_1.removeLastChar)(adapter_info.common.version, "."));
|
|
199
|
+
const previousVersion = this.persistentData.version !== "" && this.persistentData.version !== undefined ? Number.parseFloat((0, utils_1.removeLastChar)(this.persistentData.version, ".")) : 0;
|
|
200
|
+
this.logger.debug(`Handling of adapter update - currentVersion: ${currentVersion} previousVersion: ${previousVersion}`);
|
|
201
|
+
if (previousVersion < currentVersion) {
|
|
202
|
+
await (0, utils_1.handleUpdate)(this, this.logger, previousVersion);
|
|
203
|
+
this.persistentData.version = adapter_info.common.version;
|
|
204
|
+
this.writePersistentData();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
this.logger.error(`Handling of adapter update - Error:`, error);
|
|
211
|
+
}
|
|
212
|
+
let connectionType = eufy_security_client_1.P2PConnectionType.PREFER_LOCAL;
|
|
213
|
+
if (this.config.p2pConnectionType === "only_local") {
|
|
214
|
+
connectionType = eufy_security_client_1.P2PConnectionType.ONLY_LOCAL;
|
|
215
|
+
}
|
|
216
|
+
else if (this.config.p2pConnectionType === "quickest") {
|
|
217
|
+
connectionType = eufy_security_client_1.P2PConnectionType.QUICKEST;
|
|
218
|
+
}
|
|
219
|
+
const config = {
|
|
220
|
+
username: this.config.username,
|
|
221
|
+
password: this.config.password,
|
|
222
|
+
country: countryCode,
|
|
223
|
+
language: languageCode,
|
|
224
|
+
persistentDir: utils.getAbsoluteInstanceDataDir(this),
|
|
225
|
+
eventDurationSeconds: this.config.eventDuration,
|
|
226
|
+
p2pConnectionSetup: connectionType,
|
|
227
|
+
pollingIntervalMinutes: this.config.pollingInterval,
|
|
228
|
+
acceptInvitations: this.config.acceptInvitations,
|
|
229
|
+
trustedDeviceName: "IOBROKER",
|
|
230
|
+
};
|
|
231
|
+
this.eufy = new eufy_security_client_1.EufySecurity(config, this.logger);
|
|
232
|
+
this.eufy.on("station added", (station) => this.onStationAdded(station));
|
|
233
|
+
this.eufy.on("device added", (device) => this.onDeviceAdded(device));
|
|
234
|
+
this.eufy.on("station removed", (station) => this.onStationRemoved(station));
|
|
235
|
+
this.eufy.on("device removed", (device) => this.onDeviceRemoved(device));
|
|
236
|
+
this.eufy.on("push message", (messages) => this.handlePushNotification(messages));
|
|
237
|
+
this.eufy.on("push connect", () => this.onPushConnect());
|
|
238
|
+
this.eufy.on("push close", () => this.onPushClose());
|
|
239
|
+
this.eufy.on("mqtt connect", () => this.onMQTTConnect());
|
|
240
|
+
this.eufy.on("mqtt close", () => this.onMQTTClose());
|
|
241
|
+
this.eufy.on("connect", () => this.onConnect());
|
|
242
|
+
this.eufy.on("close", () => this.onClose());
|
|
243
|
+
this.eufy.on("cloud livestream start", (station, device, url) => this.onCloudLivestreamStart(station, device, url));
|
|
244
|
+
this.eufy.on("cloud livestream stop", (station, device) => this.onCloudLivestreamStop(station, device));
|
|
245
|
+
this.eufy.on("device property changed", (device, name, value) => this.onDevicePropertyChanged(device, name, value));
|
|
246
|
+
this.eufy.on("device property renewed", (device, name, value) => this.onDevicePropertyRenewed(device, name, value));
|
|
247
|
+
this.eufy.on("station command result", (station, result) => this.onStationCommandResult(station, result));
|
|
248
|
+
this.eufy.on("station download start", (station, device, metadata, videostream, audiostream) => this.onStationDownloadStart(station, device, metadata, videostream, audiostream));
|
|
249
|
+
this.eufy.on("station download finish", (station, device) => this.onStationDownloadFinish(station, device));
|
|
250
|
+
this.eufy.on("station livestream start", (station, device, metadata, videostream, audiostream) => this.onStationLivestreamStart(station, device, metadata, videostream, audiostream));
|
|
251
|
+
this.eufy.on("station livestream stop", (station, device) => this.onStationLivestreamStop(station, device));
|
|
252
|
+
this.eufy.on("station rtsp url", (station, device, value, modified) => this.onStationRTSPUrl(station, device, value, modified));
|
|
253
|
+
this.eufy.on("station property changed", (station, name, value) => this.onStationPropertyChanged(station, name, value));
|
|
254
|
+
this.eufy.on("station property renewed", (station, name, value) => this.onStationPropertyRenewed(station, name, value));
|
|
255
|
+
this.eufy.on("station connect", (station) => this.onStationConnect(station));
|
|
256
|
+
this.eufy.on("station close", (station) => this.onStationClose(station));
|
|
257
|
+
this.eufy.on("tfa request", () => this.onTFARequest());
|
|
258
|
+
this.eufy.on("captcha request", (captchaId, captcha) => this.onCaptchaRequest(captchaId, captcha));
|
|
259
|
+
//TODO: Implement station alarm event
|
|
260
|
+
//this.eufy.on("station alarm event", );
|
|
261
|
+
await this.eufy.connect();
|
|
262
|
+
}
|
|
263
|
+
writePersistentData() {
|
|
264
|
+
try {
|
|
265
|
+
fs_extra_1.default.writeFileSync(this.persistentFile, JSON.stringify(this.persistentData));
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
this.logger.error(`writePersistentData() - Error: ${error}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Is called when adapter shuts down - callback has to be called under any circumstances!
|
|
273
|
+
*/
|
|
274
|
+
async onUnload(callback) {
|
|
275
|
+
try {
|
|
276
|
+
this.writePersistentData();
|
|
277
|
+
if (this.eufy) {
|
|
278
|
+
this.eufy.removeAllListeners();
|
|
279
|
+
this.eufy.close();
|
|
280
|
+
}
|
|
281
|
+
callback();
|
|
282
|
+
}
|
|
283
|
+
catch (e) {
|
|
284
|
+
callback();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// If you need to react to object changes, uncomment the following block and the corresponding line in the constructor.
|
|
288
|
+
// You also need to subscribe to the objects with `this.subscribeObjects`, similar to `this.subscribeStates`.
|
|
289
|
+
// /**
|
|
290
|
+
// * Is called if a subscribed object changes
|
|
291
|
+
// */
|
|
292
|
+
// private onObjectChange(id: string, obj: ioBroker.Object | null | undefined): void {
|
|
293
|
+
// if (obj) {
|
|
294
|
+
// // The object was changed
|
|
295
|
+
// this.log.info(`object ${id} changed: ${JSON.stringify(obj)}`);
|
|
296
|
+
// } else {
|
|
297
|
+
// // The object was deleted
|
|
298
|
+
// this.log.info(`object ${id} deleted`);
|
|
299
|
+
// }
|
|
300
|
+
// }
|
|
301
|
+
/**
|
|
302
|
+
* Is called if a subscribed state changes
|
|
303
|
+
*/
|
|
304
|
+
async onStateChange(id, state) {
|
|
305
|
+
if (state) {
|
|
306
|
+
// don't do anything if the state is acked
|
|
307
|
+
if (!id || state.ack) {
|
|
308
|
+
this.logger.debug(`state ${id} changed: ${state.val} (ack = ${state.ack}) was already acknowledged, ignore it...`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
this.logger.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
|
|
312
|
+
const values = id.split(".");
|
|
313
|
+
const station_sn = values[2];
|
|
314
|
+
const device_type = values[3];
|
|
315
|
+
if (station_sn == "verify_code") {
|
|
316
|
+
if (this.eufy && this.verify_code) {
|
|
317
|
+
this.logger.info(`Verification code received, send it. (verify_code: ${state.val})`);
|
|
318
|
+
this.eufy.connect(state.val);
|
|
319
|
+
this.verify_code = false;
|
|
320
|
+
await this.delStateAsync(id);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else if (station_sn == "captcha") {
|
|
324
|
+
if (this.eufy && this.captchaId) {
|
|
325
|
+
this.logger.info(`Captcha received, send it. (captcha: ${state.val})`);
|
|
326
|
+
this.eufy.connect(state.val, this.captchaId);
|
|
327
|
+
this.captchaId = null;
|
|
328
|
+
await this.delStateAsync(id);
|
|
329
|
+
await this.delStateAsync("received_captcha_html");
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else if (device_type == "station") {
|
|
333
|
+
try {
|
|
334
|
+
const station_state_name = values[4];
|
|
335
|
+
if (this.eufy) {
|
|
336
|
+
const obj = await this.getObjectAsync(id);
|
|
337
|
+
if (obj) {
|
|
338
|
+
if (obj.native.name !== undefined) {
|
|
339
|
+
this.eufy.setStationProperty(station_sn, obj.native.name, state.val);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const station = this.eufy.getStation(station_sn);
|
|
344
|
+
switch (station_state_name) {
|
|
345
|
+
case types_1.StationStateID.REBOOT:
|
|
346
|
+
await station.rebootHUB();
|
|
347
|
+
break;
|
|
348
|
+
case types_1.StationStateID.TRIGGER_ALARM_SOUND:
|
|
349
|
+
await station.triggerStationAlarmSound(this.config.alarmSoundDuration);
|
|
350
|
+
break;
|
|
351
|
+
case types_1.StationStateID.RESET_ALARM_SOUND:
|
|
352
|
+
await station.resetStationAlarmSound();
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
this.logger.error(`station - Error:`, error);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
try {
|
|
363
|
+
const device_sn = values[4];
|
|
364
|
+
const obj = await this.getObjectAsync(id);
|
|
365
|
+
if (obj) {
|
|
366
|
+
if (obj.native.name !== undefined) {
|
|
367
|
+
try {
|
|
368
|
+
this.eufy.setDeviceProperty(device_sn, obj.native.name, state.val);
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
this.logger.error(`Error in setting property value`, error);
|
|
372
|
+
}
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const device_state_name = values[5];
|
|
377
|
+
const station = this.eufy.getStation(station_sn);
|
|
378
|
+
const device = this.eufy.getDevice(device_sn);
|
|
379
|
+
switch (device_state_name) {
|
|
380
|
+
case types_1.CameraStateID.START_STREAM:
|
|
381
|
+
this.startLivestream(device_sn);
|
|
382
|
+
break;
|
|
383
|
+
case types_1.CameraStateID.STOP_STREAM:
|
|
384
|
+
this.stopLivestream(device_sn);
|
|
385
|
+
break;
|
|
386
|
+
case types_1.CameraStateID.TRIGGER_ALARM_SOUND:
|
|
387
|
+
await station.triggerDeviceAlarmSound(device, this.config.alarmSoundDuration);
|
|
388
|
+
break;
|
|
389
|
+
case types_1.CameraStateID.RESET_ALARM_SOUND:
|
|
390
|
+
await station.resetDeviceAlarmSound(device);
|
|
391
|
+
break;
|
|
392
|
+
case types_1.IndoorCameraStateID.ROTATE_360:
|
|
393
|
+
await station.panAndTilt(device, eufy_security_client_1.PanTiltDirection.ROTATE360);
|
|
394
|
+
break;
|
|
395
|
+
case types_1.IndoorCameraStateID.PAN_LEFT:
|
|
396
|
+
await station.panAndTilt(device, eufy_security_client_1.PanTiltDirection.LEFT);
|
|
397
|
+
break;
|
|
398
|
+
case types_1.IndoorCameraStateID.PAN_RIGHT:
|
|
399
|
+
await station.panAndTilt(device, eufy_security_client_1.PanTiltDirection.RIGHT);
|
|
400
|
+
break;
|
|
401
|
+
case types_1.IndoorCameraStateID.TILT_UP:
|
|
402
|
+
await station.panAndTilt(device, eufy_security_client_1.PanTiltDirection.UP);
|
|
403
|
+
break;
|
|
404
|
+
case types_1.IndoorCameraStateID.TILT_DOWN:
|
|
405
|
+
await station.panAndTilt(device, eufy_security_client_1.PanTiltDirection.DOWN);
|
|
406
|
+
break;
|
|
407
|
+
case types_1.LockStateID.CALIBRATE:
|
|
408
|
+
await station.calibrateLock(device);
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
this.logger.error(`cameras - Error:`, error);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
// The state was deleted
|
|
419
|
+
this.logger.debug(`state ${id} deleted`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
// If you need to accept messages in your adapter, uncomment the following block and the corresponding line in the constructor.
|
|
423
|
+
// /**
|
|
424
|
+
// * Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...
|
|
425
|
+
// * Using this method requires "common.message" property to be set to true in io-package.json
|
|
426
|
+
// */
|
|
427
|
+
// private onMessage(obj: ioBroker.Message): void {
|
|
428
|
+
// if (typeof obj === "object" && obj.message) {
|
|
429
|
+
// if (obj.command === "send") {
|
|
430
|
+
// // e.g. send email or pushover or whatever
|
|
431
|
+
// this.log.info("send command");
|
|
432
|
+
// // Send response in callback if required
|
|
433
|
+
// if (obj.callback) this.sendTo(obj.from, obj.command, "Message received", obj.callback);
|
|
434
|
+
// }
|
|
435
|
+
// }
|
|
436
|
+
// }
|
|
437
|
+
getStateCommon(property) {
|
|
438
|
+
const state = {
|
|
439
|
+
name: property.label,
|
|
440
|
+
type: property.type,
|
|
441
|
+
role: "state",
|
|
442
|
+
read: property.readable,
|
|
443
|
+
write: property.writeable,
|
|
444
|
+
def: property.default
|
|
445
|
+
};
|
|
446
|
+
switch (property.type) {
|
|
447
|
+
case "number": {
|
|
448
|
+
const numberProperty = property;
|
|
449
|
+
state.min = numberProperty.min;
|
|
450
|
+
state.max = numberProperty.max;
|
|
451
|
+
state.states = numberProperty.states;
|
|
452
|
+
state.unit = numberProperty.unit;
|
|
453
|
+
state.step = numberProperty.steps;
|
|
454
|
+
state.role = types_1.RoleMapping[property.name] !== undefined ? types_1.RoleMapping[property.name] : "value";
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
case "string": {
|
|
458
|
+
state.role = types_1.RoleMapping[property.name] !== undefined ? types_1.RoleMapping[property.name] : "text";
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
case "boolean": {
|
|
462
|
+
state.role = types_1.RoleMapping[property.name] !== undefined ? types_1.RoleMapping[property.name] : (property.writeable ? "switch.enable" : "state");
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return state;
|
|
467
|
+
}
|
|
468
|
+
async createAndSetState(device, property) {
|
|
469
|
+
if (property.name !== eufy_security_client_1.PropertyName.Type && property.name !== eufy_security_client_1.PropertyName.DeviceStationSN) {
|
|
470
|
+
const state = this.getStateCommon(property);
|
|
471
|
+
const id = device.getStateID((0, utils_1.convertCamelCaseToSnakeCase)(property.name));
|
|
472
|
+
const obj = await this.getObjectAsync(id);
|
|
473
|
+
if (obj) {
|
|
474
|
+
let changed = false;
|
|
475
|
+
if (obj.native.name !== undefined && obj.native.name !== property.name) {
|
|
476
|
+
obj.native.name = property.name;
|
|
477
|
+
changed = true;
|
|
478
|
+
}
|
|
479
|
+
if (obj.native.key !== undefined && obj.native.key !== property.key) {
|
|
480
|
+
obj.native.key = property.key;
|
|
481
|
+
changed = true;
|
|
482
|
+
}
|
|
483
|
+
if (obj.native.commandId !== undefined && obj.native.commandId !== property.commandId) {
|
|
484
|
+
obj.native.commandId = property.commandId;
|
|
485
|
+
changed = true;
|
|
486
|
+
}
|
|
487
|
+
if (obj.common !== undefined && !util_1.default.isDeepStrictEqual(obj.common, state)) {
|
|
488
|
+
changed = true;
|
|
489
|
+
}
|
|
490
|
+
if (changed) {
|
|
491
|
+
const propertyMetadata = device.getPropertiesMetadata()[property.name];
|
|
492
|
+
if (propertyMetadata !== undefined) {
|
|
493
|
+
const newState = this.getStateCommon(propertyMetadata);
|
|
494
|
+
obj.common = newState;
|
|
495
|
+
}
|
|
496
|
+
await this.setObjectAsync(id, obj);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
await this.setObjectNotExistsAsync(id, {
|
|
501
|
+
type: "state",
|
|
502
|
+
common: state,
|
|
503
|
+
native: {
|
|
504
|
+
key: property.key,
|
|
505
|
+
commandId: property.commandId,
|
|
506
|
+
name: property.name,
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
const value = device.getPropertyValue(property.name);
|
|
511
|
+
if (value !== undefined)
|
|
512
|
+
await (0, utils_1.setStateChangedWithTimestamp)(this, id, value.value, value.timestamp);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async onDeviceAdded(device) {
|
|
516
|
+
//this.logger.debug(`count: ${Object.keys(devices).length}`);
|
|
517
|
+
await this.setObjectNotExistsAsync(device.getStateID("", 0), {
|
|
518
|
+
type: "channel",
|
|
519
|
+
common: {
|
|
520
|
+
name: device.getStateChannel()
|
|
521
|
+
},
|
|
522
|
+
native: {},
|
|
523
|
+
});
|
|
524
|
+
await this.setObjectNotExistsAsync(device.getStateID("", 1), {
|
|
525
|
+
type: "device",
|
|
526
|
+
common: {
|
|
527
|
+
name: device.getName()
|
|
528
|
+
},
|
|
529
|
+
native: {},
|
|
530
|
+
});
|
|
531
|
+
const metadata = device.getPropertiesMetadata();
|
|
532
|
+
for (const property of Object.values(metadata)) {
|
|
533
|
+
this.createAndSetState(device, property);
|
|
534
|
+
}
|
|
535
|
+
if (device.hasCommand(eufy_security_client_1.CommandName.DeviceTriggerAlarmSound)) {
|
|
536
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.TRIGGER_ALARM_SOUND), {
|
|
537
|
+
type: "state",
|
|
538
|
+
common: {
|
|
539
|
+
name: "Trigger Alarm Sound",
|
|
540
|
+
type: "boolean",
|
|
541
|
+
role: "button.start",
|
|
542
|
+
read: false,
|
|
543
|
+
write: true,
|
|
544
|
+
},
|
|
545
|
+
native: {},
|
|
546
|
+
});
|
|
547
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.RESET_ALARM_SOUND), {
|
|
548
|
+
type: "state",
|
|
549
|
+
common: {
|
|
550
|
+
name: "Reset Alarm Sound",
|
|
551
|
+
type: "boolean",
|
|
552
|
+
role: "button.start",
|
|
553
|
+
read: false,
|
|
554
|
+
write: true,
|
|
555
|
+
},
|
|
556
|
+
native: {},
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (device.hasCommand(eufy_security_client_1.CommandName.DevicePanAndTilt)) {
|
|
560
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.IndoorCameraStateID.PAN_LEFT), {
|
|
561
|
+
type: "state",
|
|
562
|
+
common: {
|
|
563
|
+
name: "Pan Left",
|
|
564
|
+
type: "boolean",
|
|
565
|
+
role: "button.start",
|
|
566
|
+
read: false,
|
|
567
|
+
write: true,
|
|
568
|
+
},
|
|
569
|
+
native: {},
|
|
570
|
+
});
|
|
571
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.IndoorCameraStateID.PAN_RIGHT), {
|
|
572
|
+
type: "state",
|
|
573
|
+
common: {
|
|
574
|
+
name: "Pan Right",
|
|
575
|
+
type: "boolean",
|
|
576
|
+
role: "button.start",
|
|
577
|
+
read: false,
|
|
578
|
+
write: true,
|
|
579
|
+
},
|
|
580
|
+
native: {},
|
|
581
|
+
});
|
|
582
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.IndoorCameraStateID.ROTATE_360), {
|
|
583
|
+
type: "state",
|
|
584
|
+
common: {
|
|
585
|
+
name: "Rotate 360°",
|
|
586
|
+
type: "boolean",
|
|
587
|
+
role: "button.start",
|
|
588
|
+
read: false,
|
|
589
|
+
write: true,
|
|
590
|
+
},
|
|
591
|
+
native: {},
|
|
592
|
+
});
|
|
593
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.IndoorCameraStateID.TILT_UP), {
|
|
594
|
+
type: "state",
|
|
595
|
+
common: {
|
|
596
|
+
name: "Tilt Up",
|
|
597
|
+
type: "boolean",
|
|
598
|
+
role: "button.start",
|
|
599
|
+
read: false,
|
|
600
|
+
write: true,
|
|
601
|
+
},
|
|
602
|
+
native: {},
|
|
603
|
+
});
|
|
604
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.IndoorCameraStateID.TILT_DOWN), {
|
|
605
|
+
type: "state",
|
|
606
|
+
common: {
|
|
607
|
+
name: "Tilt Down",
|
|
608
|
+
type: "boolean",
|
|
609
|
+
role: "button.start",
|
|
610
|
+
read: false,
|
|
611
|
+
write: true,
|
|
612
|
+
},
|
|
613
|
+
native: {},
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
if (device.hasCommand(eufy_security_client_1.CommandName.DeviceLockCalibration)) {
|
|
617
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.LockStateID.CALIBRATE), {
|
|
618
|
+
type: "state",
|
|
619
|
+
common: {
|
|
620
|
+
name: "Calibrate Lock",
|
|
621
|
+
type: "boolean",
|
|
622
|
+
role: "button.start",
|
|
623
|
+
read: false,
|
|
624
|
+
write: true,
|
|
625
|
+
},
|
|
626
|
+
native: {},
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
if (device.hasProperty(eufy_security_client_1.PropertyName.DevicePictureUrl)) {
|
|
630
|
+
// Last event picture
|
|
631
|
+
const last_camera_url = device.getPropertyValue(eufy_security_client_1.PropertyName.DevicePictureUrl);
|
|
632
|
+
if (last_camera_url !== undefined)
|
|
633
|
+
(0, utils_1.saveImageStates)(this, last_camera_url.value, last_camera_url.timestamp, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.CameraStateID.LAST_EVENT_PIC_URL), device.getStateID(types_1.CameraStateID.LAST_EVENT_PIC_HTML), "Last event picture").catch(() => {
|
|
634
|
+
this.logger.error(`State LAST_EVENT_PICTURE_URL of device ${device.getSerial()} - saveImageStates(): url ${last_camera_url.value}`);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
if (device.hasCommand(eufy_security_client_1.CommandName.DeviceStartLivestream)) {
|
|
638
|
+
// Start Stream
|
|
639
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.START_STREAM), {
|
|
640
|
+
type: "state",
|
|
641
|
+
common: {
|
|
642
|
+
name: "Start stream",
|
|
643
|
+
type: "boolean",
|
|
644
|
+
role: "button.start",
|
|
645
|
+
read: false,
|
|
646
|
+
write: true,
|
|
647
|
+
},
|
|
648
|
+
native: {},
|
|
649
|
+
});
|
|
650
|
+
// Stop Stream
|
|
651
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.STOP_STREAM), {
|
|
652
|
+
type: "state",
|
|
653
|
+
common: {
|
|
654
|
+
name: "Stop stream",
|
|
655
|
+
type: "boolean",
|
|
656
|
+
role: "button.stop",
|
|
657
|
+
read: false,
|
|
658
|
+
write: true,
|
|
659
|
+
},
|
|
660
|
+
native: {},
|
|
661
|
+
});
|
|
662
|
+
// Livestream URL
|
|
663
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.LIVESTREAM), {
|
|
664
|
+
type: "state",
|
|
665
|
+
common: {
|
|
666
|
+
name: "Livestream URL",
|
|
667
|
+
type: "string",
|
|
668
|
+
role: "url",
|
|
669
|
+
read: true,
|
|
670
|
+
write: false,
|
|
671
|
+
},
|
|
672
|
+
native: {},
|
|
673
|
+
});
|
|
674
|
+
// Last livestream video URL
|
|
675
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.LAST_LIVESTREAM_VIDEO_URL), {
|
|
676
|
+
type: "state",
|
|
677
|
+
common: {
|
|
678
|
+
name: "Last livestream video URL",
|
|
679
|
+
type: "string",
|
|
680
|
+
role: "url",
|
|
681
|
+
read: true,
|
|
682
|
+
write: false,
|
|
683
|
+
},
|
|
684
|
+
native: {},
|
|
685
|
+
});
|
|
686
|
+
// Last livestream picture URL
|
|
687
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.LAST_LIVESTREAM_PIC_URL), {
|
|
688
|
+
type: "state",
|
|
689
|
+
common: {
|
|
690
|
+
name: "Last livestream picture URL",
|
|
691
|
+
type: "string",
|
|
692
|
+
role: "url",
|
|
693
|
+
read: true,
|
|
694
|
+
write: false,
|
|
695
|
+
},
|
|
696
|
+
native: {},
|
|
697
|
+
});
|
|
698
|
+
// Last livestream picture HTML
|
|
699
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.LAST_LIVESTREAM_PIC_HTML), {
|
|
700
|
+
type: "state",
|
|
701
|
+
common: {
|
|
702
|
+
name: "Last livestream picture HTML image",
|
|
703
|
+
type: "string",
|
|
704
|
+
role: "html",
|
|
705
|
+
read: true,
|
|
706
|
+
write: false,
|
|
707
|
+
},
|
|
708
|
+
native: {},
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
if (device.hasProperty(eufy_security_client_1.PropertyName.DeviceRTSPStream)) {
|
|
712
|
+
// RTSP Stream URL
|
|
713
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.RTSP_STREAM_URL), {
|
|
714
|
+
type: "state",
|
|
715
|
+
common: {
|
|
716
|
+
name: "RTSP stream URL",
|
|
717
|
+
type: "string",
|
|
718
|
+
role: "url",
|
|
719
|
+
read: true,
|
|
720
|
+
write: false
|
|
721
|
+
},
|
|
722
|
+
native: {},
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
if (device.hasCommand(eufy_security_client_1.CommandName.DeviceStartDownload)) {
|
|
726
|
+
// Last event video URL
|
|
727
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.LAST_EVENT_VIDEO_URL), {
|
|
728
|
+
type: "state",
|
|
729
|
+
common: {
|
|
730
|
+
name: "Last event video URL",
|
|
731
|
+
type: "string",
|
|
732
|
+
role: "url",
|
|
733
|
+
read: true,
|
|
734
|
+
write: false,
|
|
735
|
+
def: ""
|
|
736
|
+
},
|
|
737
|
+
native: {},
|
|
738
|
+
});
|
|
739
|
+
// Last event picture URL
|
|
740
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.LAST_EVENT_PIC_URL), {
|
|
741
|
+
type: "state",
|
|
742
|
+
common: {
|
|
743
|
+
name: "Last event picture URL",
|
|
744
|
+
type: "string",
|
|
745
|
+
role: "url",
|
|
746
|
+
read: true,
|
|
747
|
+
write: false,
|
|
748
|
+
def: ""
|
|
749
|
+
},
|
|
750
|
+
native: {},
|
|
751
|
+
});
|
|
752
|
+
// Last event picture HTML image
|
|
753
|
+
await this.setObjectNotExistsAsync(device.getStateID(types_1.CameraStateID.LAST_EVENT_PIC_HTML), {
|
|
754
|
+
type: "state",
|
|
755
|
+
common: {
|
|
756
|
+
name: "Last event picture HTML image",
|
|
757
|
+
type: "string",
|
|
758
|
+
role: "html",
|
|
759
|
+
read: true,
|
|
760
|
+
write: false,
|
|
761
|
+
def: ""
|
|
762
|
+
},
|
|
763
|
+
native: {},
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async onDeviceRemoved(device) {
|
|
768
|
+
this.delObjectAsync(device.getStateID("", 0), { recursive: true }).catch((error) => {
|
|
769
|
+
this.logger.error(`Error deleting removed device`, error);
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
async onStationAdded(station) {
|
|
773
|
+
this.subscribeStates(`${station.getStateID("", 0)}.*`);
|
|
774
|
+
await this.setObjectNotExistsAsync(station.getStateID("", 0), {
|
|
775
|
+
type: "device",
|
|
776
|
+
common: {
|
|
777
|
+
name: station.getName()
|
|
778
|
+
},
|
|
779
|
+
native: {},
|
|
780
|
+
});
|
|
781
|
+
await this.setObjectNotExistsAsync(station.getStateID("", 1), {
|
|
782
|
+
type: "channel",
|
|
783
|
+
common: {
|
|
784
|
+
name: station.getStateChannel()
|
|
785
|
+
},
|
|
786
|
+
native: {},
|
|
787
|
+
});
|
|
788
|
+
await this.setObjectNotExistsAsync(station.getStateID(types_1.StationStateID.CONNECTION), {
|
|
789
|
+
type: "state",
|
|
790
|
+
common: {
|
|
791
|
+
name: "Connection",
|
|
792
|
+
type: "boolean",
|
|
793
|
+
role: "indicator.connection",
|
|
794
|
+
read: true,
|
|
795
|
+
write: false,
|
|
796
|
+
},
|
|
797
|
+
native: {},
|
|
798
|
+
});
|
|
799
|
+
await this.setStateAsync(station.getStateID(types_1.StationStateID.CONNECTION), { val: false, ack: true });
|
|
800
|
+
const metadata = station.getPropertiesMetadata();
|
|
801
|
+
for (const property of Object.values(metadata)) {
|
|
802
|
+
this.createAndSetState(station, property);
|
|
803
|
+
}
|
|
804
|
+
// Reboot station
|
|
805
|
+
if (station.hasCommand(eufy_security_client_1.CommandName.StationReboot)) {
|
|
806
|
+
await this.setObjectNotExistsAsync(station.getStateID(types_1.StationStateID.REBOOT), {
|
|
807
|
+
type: "state",
|
|
808
|
+
common: {
|
|
809
|
+
name: "Reboot station",
|
|
810
|
+
type: "boolean",
|
|
811
|
+
role: "button.start",
|
|
812
|
+
read: false,
|
|
813
|
+
write: true,
|
|
814
|
+
},
|
|
815
|
+
native: {},
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
// Alarm Sound
|
|
819
|
+
if (station.hasCommand(eufy_security_client_1.CommandName.StationTriggerAlarmSound)) {
|
|
820
|
+
await this.setObjectNotExistsAsync(station.getStateID(types_1.StationStateID.TRIGGER_ALARM_SOUND), {
|
|
821
|
+
type: "state",
|
|
822
|
+
common: {
|
|
823
|
+
name: "Trigger Alarm Sound",
|
|
824
|
+
type: "boolean",
|
|
825
|
+
role: "button.start",
|
|
826
|
+
read: false,
|
|
827
|
+
write: true,
|
|
828
|
+
},
|
|
829
|
+
native: {},
|
|
830
|
+
});
|
|
831
|
+
await this.setObjectNotExistsAsync(station.getStateID(types_1.StationStateID.RESET_ALARM_SOUND), {
|
|
832
|
+
type: "state",
|
|
833
|
+
common: {
|
|
834
|
+
name: "Reset Alarm Sound",
|
|
835
|
+
type: "boolean",
|
|
836
|
+
role: "button.start",
|
|
837
|
+
read: false,
|
|
838
|
+
write: true,
|
|
839
|
+
},
|
|
840
|
+
native: {},
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
async onStationRemoved(station) {
|
|
845
|
+
this.delObjectAsync(station.getStateID("", 0), { recursive: true }).catch((error) => {
|
|
846
|
+
this.logger.error(`Error deleting removed station`, error);
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
downloadEventVideo(device, event_time, full_path, cipher_id) {
|
|
850
|
+
this.logger.debug(`Device: ${device.getSerial()} full_path: ${full_path} cipher_id: ${cipher_id}`);
|
|
851
|
+
try {
|
|
852
|
+
if (!(0, utils_1.isEmpty)(full_path) && cipher_id !== undefined) {
|
|
853
|
+
const station = this.eufy.getStation(device.getStationSerial());
|
|
854
|
+
if (station !== undefined) {
|
|
855
|
+
if (this.downloadEvent[device.getSerial()])
|
|
856
|
+
clearTimeout(this.downloadEvent[device.getSerial()]);
|
|
857
|
+
let videoLength = (0, utils_1.getVideoClipLength)(device);
|
|
858
|
+
const time_passed = (new Date().getTime() - new Date(event_time).getTime()) / 1000;
|
|
859
|
+
if (time_passed >= videoLength)
|
|
860
|
+
videoLength = 1;
|
|
861
|
+
else
|
|
862
|
+
videoLength = videoLength - time_passed;
|
|
863
|
+
this.logger.info(`Downloading video event for device ${device.getSerial()} in ${videoLength} seconds...`);
|
|
864
|
+
this.downloadEvent[device.getSerial()] = setTimeout(async () => {
|
|
865
|
+
station.startDownload(device, full_path, cipher_id);
|
|
866
|
+
}, videoLength * 1000);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
catch (error) {
|
|
871
|
+
this.logger.error(`Device: ${device.getSerial()} - Error`, error);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
async handlePushNotification(message) {
|
|
875
|
+
try {
|
|
876
|
+
if (message.device_sn !== undefined) {
|
|
877
|
+
const device = this.eufy.getDevice(message.device_sn);
|
|
878
|
+
if (!(0, utils_1.isEmpty)(message.pic_url)) {
|
|
879
|
+
await (0, utils_1.saveImageStates)(this, message.pic_url, message.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.CameraStateID.LAST_EVENT_PIC_URL), device.getStateID(types_1.CameraStateID.LAST_EVENT_PIC_HTML), "Last captured picture").catch(() => {
|
|
880
|
+
this.logger.error(`Device ${device.getSerial()} - saveImageStates(): url ${message.pic_url}`);
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
if ((message.push_count === 1 || message.push_count === undefined) && (message.file_path !== undefined && message.file_path !== "" && message.cipher !== undefined))
|
|
884
|
+
if (this.config.autoDownloadVideo)
|
|
885
|
+
this.downloadEventVideo(device, message.event_time, message.file_path, message.cipher);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
catch (error) {
|
|
889
|
+
if (error instanceof eufy_security_client_1.DeviceNotFoundError) {
|
|
890
|
+
//Do nothing
|
|
891
|
+
}
|
|
892
|
+
else {
|
|
893
|
+
this.logger.error("Handling push notification - Error", error);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
async onConnect() {
|
|
898
|
+
await this.setObjectNotExistsAsync("info", {
|
|
899
|
+
type: "channel",
|
|
900
|
+
common: {
|
|
901
|
+
name: "info"
|
|
902
|
+
},
|
|
903
|
+
native: {},
|
|
904
|
+
});
|
|
905
|
+
await this.setObjectNotExistsAsync("info.connection", {
|
|
906
|
+
type: "state",
|
|
907
|
+
common: {
|
|
908
|
+
name: "Global connection",
|
|
909
|
+
type: "boolean",
|
|
910
|
+
role: "indicator.connection",
|
|
911
|
+
read: true,
|
|
912
|
+
write: false,
|
|
913
|
+
},
|
|
914
|
+
native: {},
|
|
915
|
+
});
|
|
916
|
+
await this.setStateAsync("info.connection", { val: true, ack: true });
|
|
917
|
+
}
|
|
918
|
+
async onClose() {
|
|
919
|
+
await this.setObjectNotExistsAsync("info", {
|
|
920
|
+
type: "channel",
|
|
921
|
+
common: {
|
|
922
|
+
name: "info"
|
|
923
|
+
},
|
|
924
|
+
native: {},
|
|
925
|
+
});
|
|
926
|
+
await this.setObjectNotExistsAsync("info.connection", {
|
|
927
|
+
type: "state",
|
|
928
|
+
common: {
|
|
929
|
+
name: "Global connection",
|
|
930
|
+
type: "boolean",
|
|
931
|
+
role: "indicator.connection",
|
|
932
|
+
read: true,
|
|
933
|
+
write: false,
|
|
934
|
+
},
|
|
935
|
+
native: {},
|
|
936
|
+
});
|
|
937
|
+
await this.setStateAsync("info.connection", { val: false, ack: true });
|
|
938
|
+
}
|
|
939
|
+
getPersistentData() {
|
|
940
|
+
return this.persistentData;
|
|
941
|
+
}
|
|
942
|
+
async onPushConnect() {
|
|
943
|
+
await this.setObjectNotExistsAsync("info", {
|
|
944
|
+
type: "channel",
|
|
945
|
+
common: {
|
|
946
|
+
name: "info"
|
|
947
|
+
},
|
|
948
|
+
native: {},
|
|
949
|
+
});
|
|
950
|
+
await this.setObjectNotExistsAsync("info.push_connection", {
|
|
951
|
+
type: "state",
|
|
952
|
+
common: {
|
|
953
|
+
name: "Push notification connection",
|
|
954
|
+
type: "boolean",
|
|
955
|
+
role: "indicator.connection",
|
|
956
|
+
read: true,
|
|
957
|
+
write: false,
|
|
958
|
+
},
|
|
959
|
+
native: {},
|
|
960
|
+
});
|
|
961
|
+
await this.setStateAsync("info.push_connection", { val: true, ack: true });
|
|
962
|
+
}
|
|
963
|
+
async onPushClose() {
|
|
964
|
+
await this.setObjectNotExistsAsync("info", {
|
|
965
|
+
type: "channel",
|
|
966
|
+
common: {
|
|
967
|
+
name: "info"
|
|
968
|
+
},
|
|
969
|
+
native: {},
|
|
970
|
+
});
|
|
971
|
+
await this.setObjectNotExistsAsync("info.push_connection", {
|
|
972
|
+
type: "state",
|
|
973
|
+
common: {
|
|
974
|
+
name: "Push notification connection",
|
|
975
|
+
type: "boolean",
|
|
976
|
+
role: "indicator.connection",
|
|
977
|
+
read: true,
|
|
978
|
+
write: false,
|
|
979
|
+
},
|
|
980
|
+
native: {},
|
|
981
|
+
});
|
|
982
|
+
await this.setStateAsync("info.push_connection", { val: false, ack: true });
|
|
983
|
+
}
|
|
984
|
+
async onMQTTConnect() {
|
|
985
|
+
await this.setObjectNotExistsAsync("info", {
|
|
986
|
+
type: "channel",
|
|
987
|
+
common: {
|
|
988
|
+
name: "info"
|
|
989
|
+
},
|
|
990
|
+
native: {},
|
|
991
|
+
});
|
|
992
|
+
await this.setObjectNotExistsAsync("info.mqtt_connection", {
|
|
993
|
+
type: "state",
|
|
994
|
+
common: {
|
|
995
|
+
name: "MQTT connection",
|
|
996
|
+
type: "boolean",
|
|
997
|
+
role: "indicator.connection",
|
|
998
|
+
read: true,
|
|
999
|
+
write: false,
|
|
1000
|
+
},
|
|
1001
|
+
native: {},
|
|
1002
|
+
});
|
|
1003
|
+
await this.setStateAsync("info.mqtt_connection", { val: true, ack: true });
|
|
1004
|
+
}
|
|
1005
|
+
async onMQTTClose() {
|
|
1006
|
+
await this.setObjectNotExistsAsync("info", {
|
|
1007
|
+
type: "channel",
|
|
1008
|
+
common: {
|
|
1009
|
+
name: "info"
|
|
1010
|
+
},
|
|
1011
|
+
native: {},
|
|
1012
|
+
});
|
|
1013
|
+
await this.setObjectNotExistsAsync("info.mqtt_connection", {
|
|
1014
|
+
type: "state",
|
|
1015
|
+
common: {
|
|
1016
|
+
name: "MQTT connection",
|
|
1017
|
+
type: "boolean",
|
|
1018
|
+
role: "indicator.connection",
|
|
1019
|
+
read: true,
|
|
1020
|
+
write: false,
|
|
1021
|
+
},
|
|
1022
|
+
native: {},
|
|
1023
|
+
});
|
|
1024
|
+
await this.setStateAsync("info.mqtt_connection", { val: false, ack: true });
|
|
1025
|
+
}
|
|
1026
|
+
async onStationCommandResult(station, result) {
|
|
1027
|
+
if (result.return_code !== 0 && result.command_type === eufy_security_client_1.CommandType.CMD_START_REALTIME_MEDIA) {
|
|
1028
|
+
this.logger.debug(`Station: ${station.getSerial()} command ${eufy_security_client_1.CommandType[result.command_type]} failed with error: ${eufy_security_client_1.ErrorCode[result.return_code]} (${result.return_code}) fallback to RTMP livestream...`);
|
|
1029
|
+
try {
|
|
1030
|
+
const device = this.eufy.getStationDevice(station.getSerial(), result.channel);
|
|
1031
|
+
if (device.isCamera())
|
|
1032
|
+
this.eufy.startCloudLivestream(device.getSerial());
|
|
1033
|
+
}
|
|
1034
|
+
catch (error) {
|
|
1035
|
+
this.logger.error(`Station: ${station.getSerial()} command ${eufy_security_client_1.CommandType[result.command_type]} RTMP fallback failed - Error ${error}`);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
else if (result.return_code !== 0 && result.command_type !== eufy_security_client_1.CommandType.P2P_QUERY_STATUS_IN_LOCK) {
|
|
1039
|
+
this.logger.error(`Station: ${station.getSerial()} command ${eufy_security_client_1.CommandType[result.command_type]} failed with error: ${eufy_security_client_1.ErrorCode[result.return_code]} (${result.return_code})`);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
async onStationPropertyChanged(station, name, value) {
|
|
1043
|
+
const states = await this.getStatesAsync(`${station.getStateID("", 1)}.*`);
|
|
1044
|
+
for (const state in states) {
|
|
1045
|
+
const obj = await this.getObjectAsync(state);
|
|
1046
|
+
if (obj) {
|
|
1047
|
+
if (obj.native.name !== undefined && obj.native.name === name) {
|
|
1048
|
+
(0, utils_1.setStateChangedWithTimestamp)(this, state, value.value, value.timestamp);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
this.logger.debug(`onStationPropertyChanged(): Property "${name}" not implemented in this adapter (station: ${station.getSerial()} value: ${JSON.stringify(value)})`);
|
|
1054
|
+
}
|
|
1055
|
+
async onStationPropertyRenewed(station, name, value) {
|
|
1056
|
+
const states = await this.getStatesAsync(`${station.getStateID("", 1)}.*`);
|
|
1057
|
+
for (const state in states) {
|
|
1058
|
+
const obj = await this.getObjectAsync(state);
|
|
1059
|
+
if (obj) {
|
|
1060
|
+
if (obj.native.name !== undefined && obj.native.name === name) {
|
|
1061
|
+
(0, utils_1.setStateChangedWithTimestamp)(this, state, value.value, value.timestamp);
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
this.logger.debug(`onStationPropertyRenewed(): Property "${name}" not implemented in this adapter (station: ${station.getSerial()} value: ${JSON.stringify(value)})`);
|
|
1067
|
+
}
|
|
1068
|
+
async onDevicePropertyChanged(device, name, value) {
|
|
1069
|
+
const states = await this.getStatesAsync(`${device.getStateID("", 1)}.*`);
|
|
1070
|
+
for (const state in states) {
|
|
1071
|
+
const obj = await this.getObjectAsync(state);
|
|
1072
|
+
if (obj) {
|
|
1073
|
+
if (obj.native.name !== undefined && obj.native.name === name) {
|
|
1074
|
+
(0, utils_1.setStateChangedWithTimestamp)(this, state, value.value, value.timestamp);
|
|
1075
|
+
switch (name) {
|
|
1076
|
+
case eufy_security_client_1.PropertyName.DeviceRTSPStream:
|
|
1077
|
+
if (value.value === false) {
|
|
1078
|
+
this.delStateAsync(device.getStateID(types_1.CameraStateID.RTSP_STREAM_URL));
|
|
1079
|
+
}
|
|
1080
|
+
break;
|
|
1081
|
+
}
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
this.logger.debug(`onDevicePropertyChanged(): Property "${name}" not implemented in this adapter (device: ${device.getSerial()} value: ${JSON.stringify(value)})`);
|
|
1087
|
+
}
|
|
1088
|
+
async onDevicePropertyRenewed(device, name, value) {
|
|
1089
|
+
const states = await this.getStatesAsync(`${device.getStateID("", 1)}.*`);
|
|
1090
|
+
for (const state in states) {
|
|
1091
|
+
const obj = await this.getObjectAsync(state);
|
|
1092
|
+
if (obj) {
|
|
1093
|
+
if (obj.native.name !== undefined && obj.native.name === name) {
|
|
1094
|
+
(0, utils_1.setStateChangedWithTimestamp)(this, state, value.value, value.timestamp);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
this.logger.debug(`onDevicePropertyRenewed(): Property "${name}" not implemented in this adapter (device: ${device.getSerial()} value: ${JSON.stringify(value)})`);
|
|
1100
|
+
}
|
|
1101
|
+
async startLivestream(device_sn) {
|
|
1102
|
+
try {
|
|
1103
|
+
const device = this.eufy.getDevice(device_sn);
|
|
1104
|
+
const station = this.eufy.getStation(device.getStationSerial());
|
|
1105
|
+
if (station.isConnected() || station.isEnergySavingDevice()) {
|
|
1106
|
+
if (!station.isLiveStreaming(device)) {
|
|
1107
|
+
this.eufy.startStationLivestream(device_sn);
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
this.logger.warn(`The stream for the device ${device_sn} cannot be started, because it is already streaming!`);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
else if (device.isCamera()) {
|
|
1114
|
+
const camera = device;
|
|
1115
|
+
if (!camera.isStreaming()) {
|
|
1116
|
+
this.eufy.startCloudLivestream(device_sn);
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
this.logger.warn(`The stream for the device ${device_sn} cannot be started, because it is already streaming!`);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
catch (error) {
|
|
1124
|
+
this.logger.error("Start livestream - Error", error);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
async stopLivestream(device_sn) {
|
|
1128
|
+
try {
|
|
1129
|
+
const device = this.eufy.getDevice(device_sn);
|
|
1130
|
+
const station = this.eufy.getStation(device.getStationSerial());
|
|
1131
|
+
if (device.isCamera()) {
|
|
1132
|
+
const camera = device;
|
|
1133
|
+
if (this.eufy.isStationConnected(device.getStationSerial()) && station.isLiveStreaming(camera)) {
|
|
1134
|
+
await this.eufy.stopStationLivestream(device_sn);
|
|
1135
|
+
}
|
|
1136
|
+
else if (camera.isStreaming()) {
|
|
1137
|
+
await this.eufy.stopCloudLivestream(device_sn);
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1140
|
+
this.logger.warn(`The stream for the device ${device_sn} cannot be stopped, because it isn't streaming!`);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
catch (error) {
|
|
1145
|
+
this.logger.error("Stop livestream - Error", error);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
async onCloudLivestreamStart(station, device, url) {
|
|
1149
|
+
this.setStateAsync(device.getStateID(types_1.CameraStateID.LIVESTREAM), { val: url, ack: true });
|
|
1150
|
+
const file_path = (0, utils_1.getDataFilePath)(this, station.getSerial(), types_1.DataLocation.LIVESTREAM, `${device.getSerial()}${types_1.STREAM_FILE_NAME_EXT}`);
|
|
1151
|
+
await (0, utils_1.sleep)(2000);
|
|
1152
|
+
const rtmpPromise = (0, video_1.ffmpegRTMPToHls)(this.config, url, file_path, this.logger);
|
|
1153
|
+
rtmpPromise.then(async () => {
|
|
1154
|
+
if (fs_extra_1.default.pathExistsSync(file_path)) {
|
|
1155
|
+
await (0, utils_1.removeFiles)(this, station.getSerial(), types_1.DataLocation.LAST_LIVESTREAM, device.getSerial());
|
|
1156
|
+
return true;
|
|
1157
|
+
}
|
|
1158
|
+
return false;
|
|
1159
|
+
})
|
|
1160
|
+
.then(async (result) => {
|
|
1161
|
+
if (result)
|
|
1162
|
+
await (0, utils_1.moveFiles)(this, station.getSerial(), device.getSerial(), types_1.DataLocation.LIVESTREAM, types_1.DataLocation.LAST_LIVESTREAM);
|
|
1163
|
+
return result;
|
|
1164
|
+
})
|
|
1165
|
+
.then(async (result) => {
|
|
1166
|
+
if (result) {
|
|
1167
|
+
const filename_without_ext = (0, utils_1.getDataFilePath)(this, station.getSerial(), types_1.DataLocation.LAST_LIVESTREAM, device.getSerial());
|
|
1168
|
+
if (fs_extra_1.default.pathExistsSync(`${filename_without_ext}${types_1.STREAM_FILE_NAME_EXT}`))
|
|
1169
|
+
await (0, video_1.ffmpegPreviewImage)(this.config, `${filename_without_ext}${types_1.STREAM_FILE_NAME_EXT}`, `${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`, this.logger, 5.5)
|
|
1170
|
+
.then(() => {
|
|
1171
|
+
this.setStateAsync(device.getStateID(types_1.CameraStateID.LAST_LIVESTREAM_PIC_URL), { val: `/${this.namespace}/${station.getSerial()}/${types_1.DataLocation.LAST_LIVESTREAM}/${device.getSerial()}${types_1.IMAGE_FILE_JPEG_EXT}`, ack: true });
|
|
1172
|
+
try {
|
|
1173
|
+
if (fs_extra_1.default.existsSync(`${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`)) {
|
|
1174
|
+
this.setStateAsync(device.getStateID(types_1.CameraStateID.LAST_LIVESTREAM_PIC_HTML), { val: (0, utils_1.getImageAsHTML)(fs_extra_1.default.readFileSync(`${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`)), ack: true });
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
catch (error) {
|
|
1178
|
+
this.logger.error(`Station: ${station.getSerial()} device: ${device.getSerial()} - Error`, error);
|
|
1179
|
+
}
|
|
1180
|
+
})
|
|
1181
|
+
.catch((error) => {
|
|
1182
|
+
this.logger.error(`ffmpegPreviewImage - station: ${station.getSerial()} device: ${device.getSerial()} - Error`, error);
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
})
|
|
1186
|
+
.catch(async (error) => {
|
|
1187
|
+
this.logger.error(`Station: ${station.getSerial()} device: ${device.getSerial()} - Error - Stopping livestream...`, error);
|
|
1188
|
+
await this.eufy.stopCloudLivestream(device.getSerial());
|
|
1189
|
+
});
|
|
1190
|
+
this.rtmpFFmpegPromise.set(device.getSerial(), rtmpPromise);
|
|
1191
|
+
}
|
|
1192
|
+
onCloudLivestreamStop(station, device) {
|
|
1193
|
+
this.logger.debug(`Station: ${station.getSerial()} device: ${device.getSerial()}`);
|
|
1194
|
+
this.delStateAsync(device.getStateID(types_1.CameraStateID.LIVESTREAM));
|
|
1195
|
+
const rtmpPromise = this.rtmpFFmpegPromise.get(device.getSerial());
|
|
1196
|
+
if (rtmpPromise) {
|
|
1197
|
+
rtmpPromise.stop();
|
|
1198
|
+
this.rtmpFFmpegPromise.delete(device.getSerial());
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
async onStationLivestreamStart(station, device, metadata, videostream, audiostream) {
|
|
1202
|
+
try {
|
|
1203
|
+
const file_path = (0, utils_1.getDataFilePath)(this, station.getSerial(), types_1.DataLocation.LIVESTREAM, `${device.getSerial()}${types_1.STREAM_FILE_NAME_EXT}`);
|
|
1204
|
+
await (0, utils_1.removeFiles)(this, station.getSerial(), types_1.DataLocation.LIVESTREAM, device.getSerial()).catch();
|
|
1205
|
+
this.setStateAsync(device.getStateID(types_1.CameraStateID.LIVESTREAM), { val: `/${this.namespace}/${station.getSerial()}/${types_1.DataLocation.LIVESTREAM}/${device.getSerial()}${types_1.STREAM_FILE_NAME_EXT}`, ack: true });
|
|
1206
|
+
await (0, video_1.ffmpegStreamToHls)(this.config, this.namespace, metadata, videostream, audiostream, file_path, this.logger)
|
|
1207
|
+
.then(async () => {
|
|
1208
|
+
if (fs_extra_1.default.pathExistsSync(file_path)) {
|
|
1209
|
+
await (0, utils_1.removeFiles)(this, station.getSerial(), types_1.DataLocation.LAST_LIVESTREAM, device.getSerial());
|
|
1210
|
+
return true;
|
|
1211
|
+
}
|
|
1212
|
+
return false;
|
|
1213
|
+
})
|
|
1214
|
+
.then(async (result) => {
|
|
1215
|
+
if (result)
|
|
1216
|
+
await (0, utils_1.moveFiles)(this, station.getSerial(), device.getSerial(), types_1.DataLocation.LIVESTREAM, types_1.DataLocation.LAST_LIVESTREAM);
|
|
1217
|
+
return result;
|
|
1218
|
+
})
|
|
1219
|
+
.then(async (result) => {
|
|
1220
|
+
if (result) {
|
|
1221
|
+
const filename_without_ext = (0, utils_1.getDataFilePath)(this, station.getSerial(), types_1.DataLocation.LAST_LIVESTREAM, device.getSerial());
|
|
1222
|
+
this.setStateAsync(device.getStateID(types_1.CameraStateID.LAST_LIVESTREAM_VIDEO_URL), { val: `/${this.namespace}/${station.getSerial()}/${types_1.DataLocation.LAST_LIVESTREAM}/${device.getSerial()}${types_1.STREAM_FILE_NAME_EXT}`, ack: true });
|
|
1223
|
+
if (fs_extra_1.default.pathExistsSync(`${filename_without_ext}${types_1.STREAM_FILE_NAME_EXT}`))
|
|
1224
|
+
await (0, video_1.ffmpegPreviewImage)(this.config, `${filename_without_ext}${types_1.STREAM_FILE_NAME_EXT}`, `${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`, this.logger)
|
|
1225
|
+
.then(() => {
|
|
1226
|
+
this.setStateAsync(device.getStateID(types_1.CameraStateID.LAST_LIVESTREAM_PIC_URL), { val: `/${this.namespace}/${station.getSerial()}/${types_1.DataLocation.LAST_LIVESTREAM}/${device.getSerial()}${types_1.IMAGE_FILE_JPEG_EXT}`, ack: true });
|
|
1227
|
+
try {
|
|
1228
|
+
if (fs_extra_1.default.existsSync(`${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`)) {
|
|
1229
|
+
this.setStateAsync(device.getStateID(types_1.CameraStateID.LAST_LIVESTREAM_PIC_HTML), { val: (0, utils_1.getImageAsHTML)(fs_extra_1.default.readFileSync(`${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`)), ack: true });
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
catch (error) {
|
|
1233
|
+
this.logger.error(`Station: ${station.getSerial()} device: ${device.getSerial()} - Error`, error);
|
|
1234
|
+
}
|
|
1235
|
+
})
|
|
1236
|
+
.catch((error) => {
|
|
1237
|
+
this.logger.error(`ffmpegPreviewImage - station: ${station.getSerial()} device: ${device.getSerial()} - Error`, error);
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
})
|
|
1241
|
+
.catch(async (error) => {
|
|
1242
|
+
this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Stopping livestream...`, error);
|
|
1243
|
+
await this.eufy.stopStationLivestream(device.getSerial());
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
catch (error) {
|
|
1247
|
+
this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Stopping livestream...`, error);
|
|
1248
|
+
await this.eufy.stopStationLivestream(device.getSerial());
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
onStationLivestreamStop(_station, device) {
|
|
1252
|
+
this.delStateAsync(device.getStateID(types_1.CameraStateID.LIVESTREAM));
|
|
1253
|
+
}
|
|
1254
|
+
async onStationDownloadFinish(_station, _device) {
|
|
1255
|
+
//this.logger.trace(`Station: ${station.getSerial()} channel: ${channel}`);
|
|
1256
|
+
}
|
|
1257
|
+
async onStationDownloadStart(station, device, metadata, videostream, audiostream) {
|
|
1258
|
+
try {
|
|
1259
|
+
await (0, utils_1.removeFiles)(this, station.getSerial(), types_1.DataLocation.TEMP, device.getSerial()).catch();
|
|
1260
|
+
const file_path = (0, utils_1.getDataFilePath)(this, station.getSerial(), types_1.DataLocation.TEMP, `${device.getSerial()}${types_1.STREAM_FILE_NAME_EXT}`);
|
|
1261
|
+
await (0, video_1.ffmpegStreamToHls)(this.config, this.namespace, metadata, videostream, audiostream, file_path, this.logger)
|
|
1262
|
+
.then(async () => {
|
|
1263
|
+
if (fs_extra_1.default.pathExistsSync(file_path)) {
|
|
1264
|
+
await (0, utils_1.removeFiles)(this, station.getSerial(), types_1.DataLocation.LAST_EVENT, device.getSerial());
|
|
1265
|
+
return true;
|
|
1266
|
+
}
|
|
1267
|
+
return false;
|
|
1268
|
+
})
|
|
1269
|
+
.then(async (result) => {
|
|
1270
|
+
if (result)
|
|
1271
|
+
await (0, utils_1.moveFiles)(this, station.getSerial(), device.getSerial(), types_1.DataLocation.TEMP, types_1.DataLocation.LAST_EVENT);
|
|
1272
|
+
return result;
|
|
1273
|
+
})
|
|
1274
|
+
.then(async (result) => {
|
|
1275
|
+
if (result) {
|
|
1276
|
+
const filename_without_ext = (0, utils_1.getDataFilePath)(this, station.getSerial(), types_1.DataLocation.LAST_EVENT, device.getSerial());
|
|
1277
|
+
(0, utils_1.setStateWithTimestamp)(this, device.getStateID(types_1.CameraStateID.LAST_EVENT_VIDEO_URL), "Last captured video URL", `/${this.namespace}/${station.getSerial()}/${types_1.DataLocation.LAST_EVENT}/${device.getSerial()}${types_1.STREAM_FILE_NAME_EXT}`, undefined, "url");
|
|
1278
|
+
if (fs_extra_1.default.pathExistsSync(`${filename_without_ext}${types_1.STREAM_FILE_NAME_EXT}`))
|
|
1279
|
+
await (0, video_1.ffmpegPreviewImage)(this.config, `${filename_without_ext}${types_1.STREAM_FILE_NAME_EXT}`, `${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`, this.logger)
|
|
1280
|
+
.then(() => {
|
|
1281
|
+
(0, utils_1.setStateWithTimestamp)(this, device.getStateID(types_1.CameraStateID.LAST_EVENT_PIC_URL), "Last event picture URL", `/${this.namespace}/${station.getSerial()}/${types_1.DataLocation.LAST_EVENT}/${device.getSerial()}${types_1.IMAGE_FILE_JPEG_EXT}`, undefined, "url");
|
|
1282
|
+
try {
|
|
1283
|
+
if (fs_extra_1.default.existsSync(`${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`)) {
|
|
1284
|
+
const image_data = (0, utils_1.getImageAsHTML)(fs_extra_1.default.readFileSync(`${filename_without_ext}${types_1.IMAGE_FILE_JPEG_EXT}`));
|
|
1285
|
+
(0, utils_1.setStateWithTimestamp)(this, device.getStateID(types_1.CameraStateID.LAST_EVENT_PIC_HTML), "Last event picture HTML image", image_data, undefined, "html");
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
catch (error) {
|
|
1289
|
+
this.logger.error(`Station: ${station.getSerial()} device: ${device.getSerial()} - Error`, error);
|
|
1290
|
+
}
|
|
1291
|
+
})
|
|
1292
|
+
.catch((error) => {
|
|
1293
|
+
this.logger.error(`ffmpegPreviewImage - station: ${station.getSerial()} device: ${device.getSerial()} - Error`, error);
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
})
|
|
1297
|
+
.catch(async (error) => {
|
|
1298
|
+
this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Cancelling download...`, error);
|
|
1299
|
+
await this.eufy.cancelStationDownload(device.getSerial());
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
catch (error) {
|
|
1303
|
+
this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Cancelling download...`, error);
|
|
1304
|
+
await this.eufy.cancelStationDownload(device.getSerial());
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
onStationRTSPUrl(station, device, value, modified) {
|
|
1308
|
+
(0, utils_1.setStateChangedWithTimestamp)(this, device.getStateID(types_1.CameraStateID.RTSP_STREAM_URL), value, modified);
|
|
1309
|
+
}
|
|
1310
|
+
async onStationConnect(station) {
|
|
1311
|
+
await this.setObjectNotExistsAsync(station.getStateID(types_1.StationStateID.CONNECTION), {
|
|
1312
|
+
type: "state",
|
|
1313
|
+
common: {
|
|
1314
|
+
name: "Connection",
|
|
1315
|
+
type: "boolean",
|
|
1316
|
+
role: "indicator.connection",
|
|
1317
|
+
read: true,
|
|
1318
|
+
write: false,
|
|
1319
|
+
},
|
|
1320
|
+
native: {},
|
|
1321
|
+
});
|
|
1322
|
+
await this.setStateAsync(station.getStateID(types_1.StationStateID.CONNECTION), { val: true, ack: true });
|
|
1323
|
+
}
|
|
1324
|
+
async onStationClose(station) {
|
|
1325
|
+
await this.setObjectNotExistsAsync(station.getStateID(types_1.StationStateID.CONNECTION), {
|
|
1326
|
+
type: "state",
|
|
1327
|
+
common: {
|
|
1328
|
+
name: "Connection",
|
|
1329
|
+
type: "boolean",
|
|
1330
|
+
role: "indicator.connection",
|
|
1331
|
+
read: true,
|
|
1332
|
+
write: false,
|
|
1333
|
+
},
|
|
1334
|
+
native: {},
|
|
1335
|
+
});
|
|
1336
|
+
await this.setStateAsync(station.getStateID(types_1.StationStateID.CONNECTION), { val: false, ack: true });
|
|
1337
|
+
}
|
|
1338
|
+
onTFARequest() {
|
|
1339
|
+
this.logger.warn(`Two factor authentication request received, please enter valid verification code in state ${this.namespace}.verify_code`);
|
|
1340
|
+
this.verify_code = true;
|
|
1341
|
+
}
|
|
1342
|
+
onCaptchaRequest(captchaId, captcha) {
|
|
1343
|
+
this.captchaId = captchaId;
|
|
1344
|
+
this.logger.warn(`Captcha authentication request received, please enter valid captcha in state ${this.namespace}.captcha`);
|
|
1345
|
+
this.logger.warn(`Captcha: <img src="${captcha}">`);
|
|
1346
|
+
this.setStateAsync("received_captcha_html", { val: `<img src="${captcha}">`, ack: true });
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
exports.euSec = euSec;
|
|
1350
|
+
if (module.parent) {
|
|
1351
|
+
// Export the constructor in compact mode
|
|
1352
|
+
module.exports = (options) => new euSec(options);
|
|
1353
|
+
}
|
|
1354
|
+
else {
|
|
1355
|
+
// otherwise start the instance directly
|
|
1356
|
+
(() => new euSec())();
|
|
1357
|
+
}
|
|
1358
|
+
//# sourceMappingURL=main.js.map
|