iobroker.eusec 2.0.2 → 3.0.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/build/main.js CHANGED
@@ -31,20 +31,26 @@ __export(main_exports, {
31
31
  euSec: () => euSec
32
32
  });
33
33
  module.exports = __toCommonJS(main_exports);
34
- var utils = __toESM(require("@iobroker/adapter-core"));
35
- var path = __toESM(require("path"));
34
+ var import_adapter_core = require("@iobroker/adapter-core");
35
+ var path = __toESM(require("node:path"));
36
36
  var import_eufy_security_client = require("eufy-security-client");
37
37
  var import_i18n_iso_countries = require("i18n-iso-countries");
38
38
  var import_i18n_iso_languages = require("@cospired/i18n-iso-languages");
39
- var import_util = __toESM(require("util"));
40
- var import_child_process = __toESM(require("child_process"));
39
+ var import_node_util = __toESM(require("node:util"));
40
+ var import_node_child_process = __toESM(require("node:child_process"));
41
41
  var import_go2rtc_static = __toESM(require("go2rtc-static"));
42
42
  var import_ffmpeg_for_homebridge = __toESM(require("ffmpeg-for-homebridge"));
43
43
  var import_types = require("./lib/types");
44
44
  var import_utils = require("./lib/utils");
45
45
  var import_log = require("./lib/log");
46
+ var import_eufyApiCompat = require("./lib/eufyApiCompat");
47
+ var import_go2rtc = require("./lib/go2rtc");
46
48
  var import_video = require("./lib/video");
47
- class euSec extends utils.Adapter {
49
+ const GO2RTC_RESTART_DELAY_MIN = 1e3;
50
+ const GO2RTC_RESTART_DELAY_MAX = 3e4;
51
+ const GO2RTC_HEALTHY_RUNTIME = 6e4;
52
+ const VIDEO_STREAMING_QUALITY_AUTO_LABEL = "Auto";
53
+ class euSec extends import_adapter_core.Adapter {
48
54
  eufy;
49
55
  /*private downloadEvent: {
50
56
  [index: string]: NodeJS.Timeout;
@@ -58,6 +64,10 @@ class euSec extends utils.Adapter {
58
64
  captchaId = null;
59
65
  verify_code = false;
60
66
  skipInit = false;
67
+ go2rtcProcess = void 0;
68
+ go2rtcRestartTimeout = void 0;
69
+ go2rtcRestarts = 0;
70
+ terminating = false;
61
71
  constructor(options = {}) {
62
72
  super({
63
73
  ...options,
@@ -67,6 +77,10 @@ class euSec extends utils.Adapter {
67
77
  this.on("stateChange", this.onStateChange.bind(this));
68
78
  this.on("message", this.onMessage.bind(this));
69
79
  this.on("unload", this.onUnload.bind(this));
80
+ process.on("exit", () => {
81
+ var _a;
82
+ return (_a = this.go2rtcProcess) == null ? void 0 : _a.kill();
83
+ });
70
84
  }
71
85
  restartAdapter() {
72
86
  this.skipInit = true;
@@ -76,7 +90,6 @@ class euSec extends utils.Adapter {
76
90
  * Is called when databases are connected and adapter received configuration.
77
91
  */
78
92
  async onReady() {
79
- var _a;
80
93
  this.logger = new import_log.ioBrokerLogger(this.log);
81
94
  await this.setObjectNotExistsAsync("verify_code", {
82
95
  type: "state",
@@ -156,10 +169,11 @@ class euSec extends utils.Adapter {
156
169
  await this.setStateAsync("info.mqtt_connection", { val: false, ack: true });
157
170
  try {
158
171
  const connection = await this.getStatesAsync("*.connection");
159
- if (connection)
172
+ if (connection) {
160
173
  Object.keys(connection).forEach(async (id) => {
161
174
  await this.setStateAsync(id, { val: false, ack: true });
162
175
  });
176
+ }
163
177
  } catch (error) {
164
178
  this.logger.error("Reset connection states - Error", error);
165
179
  }
@@ -174,10 +188,11 @@ class euSec extends utils.Adapter {
174
188
  ];
175
189
  for (const sensorName of sensorList) {
176
190
  const sensors = await this.getStatesAsync(`*.${(0, import_utils.convertCamelCaseToSnakeCase)(sensorName)}`);
177
- if (sensors)
191
+ if (sensors) {
178
192
  Object.keys(sensors).forEach(async (id) => {
179
193
  await this.setStateAsync(id, { val: false, ack: true });
180
194
  });
195
+ }
181
196
  }
182
197
  } catch (error) {
183
198
  this.logger.error("Reset sensor states - Error", error);
@@ -206,31 +221,6 @@ class euSec extends utils.Adapter {
206
221
  if (this.config.hostname === "") {
207
222
  this.config.hostname = Object.values(hosts)[0].native.os.hostname;
208
223
  }
209
- const nodeVersion = Object.values(hosts)[0].native.process.versions.node;
210
- const nodeMajorVersion = nodeVersion.split(".")[0];
211
- let fixNeeded;
212
- switch (parseInt(nodeMajorVersion)) {
213
- // node 18 is no longer supported
214
- case 20:
215
- fixNeeded = nodeVersion.localeCompare("20.11.1", void 0, { numeric: true, sensitivity: "base" });
216
- break;
217
- // node 21 is and was not supported
218
- // node 22 and newer do NOT require any fix
219
- default:
220
- fixNeeded = -1;
221
- break;
222
- }
223
- if (fixNeeded >= 0) {
224
- const adapter = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
225
- if (adapter !== void 0 && adapter !== null) {
226
- if (!((_a = adapter.common.nodeProcessParams) == null ? void 0 : _a.includes("--security-revert=CVE-2023-46809"))) {
227
- adapter.common.nodeProcessParams = ["--security-revert=CVE-2023-46809"];
228
- await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, adapter);
229
- this.log.warn("Required fix to use livestreaming with this version of Node.js (CVE-2023-46809) applied. Restart of the adapter initiated to activate the fix.");
230
- this.restartAdapter();
231
- }
232
- }
233
- }
234
224
  }
235
225
  if (!this.skipInit) {
236
226
  const systemConfig = await this.getForeignObjectAsync("system.config");
@@ -238,8 +228,9 @@ class euSec extends utils.Adapter {
238
228
  let languageCode = void 0;
239
229
  if (systemConfig) {
240
230
  countryCode = (0, import_i18n_iso_countries.getAlpha2Code)(systemConfig.common.country, "en");
241
- if ((0, import_i18n_iso_languages.isValid)(systemConfig.common.language))
231
+ if ((0, import_i18n_iso_languages.isValid)(systemConfig.common.language)) {
242
232
  languageCode = systemConfig.common.language;
233
+ }
243
234
  }
244
235
  if (this.config.country !== "iobroker") {
245
236
  countryCode = this.config.country;
@@ -248,7 +239,9 @@ class euSec extends utils.Adapter {
248
239
  if (this.persistentData.version !== this.version) {
249
240
  const currentVersion = Number.parseFloat((0, import_utils.removeLastChar)(this.version, "."));
250
241
  const previousVersion = this.persistentData.version !== "" && this.persistentData.version !== void 0 ? Number.parseFloat((0, import_utils.removeLastChar)(this.persistentData.version, ".")) : 0;
251
- this.logger.debug(`Handling of adapter update - currentVersion: ${currentVersion} previousVersion: ${previousVersion}`);
242
+ this.logger.debug(
243
+ `Handling of adapter update - currentVersion: ${currentVersion} previousVersion: ${previousVersion}`
244
+ );
252
245
  if (previousVersion < currentVersion) {
253
246
  await (0, import_utils.handleUpdate)(this, this.logger, previousVersion, currentVersion);
254
247
  this.persistentData.version = this.version;
@@ -277,6 +270,7 @@ class euSec extends utils.Adapter {
277
270
  level: this.log.level === "silly" ? import_eufy_security_client.LogLevel.Trace : this.log.level === "debug" ? import_eufy_security_client.LogLevel.Debug : this.log.level === "info" ? import_eufy_security_client.LogLevel.Info : this.log.level === "warn" ? import_eufy_security_client.LogLevel.Warn : this.log.level === "error" ? import_eufy_security_client.LogLevel.Error : import_eufy_security_client.LogLevel.Info
278
271
  }
279
272
  };
273
+ (0, import_eufyApiCompat.applyEufyApiCompatibility)((message) => this.logger.info(message));
280
274
  this.eufy = await import_eufy_security_client.EufySecurity.initialize(config, this.logger);
281
275
  this.eufy.on("persistent data", (data) => this.onPersistentData(data));
282
276
  this.eufy.on("station added", (station) => this.onStationAdded(station));
@@ -290,37 +284,57 @@ class euSec extends utils.Adapter {
290
284
  this.eufy.on("mqtt close", () => this.onMQTTClose());
291
285
  this.eufy.on("connect", () => this.onConnect());
292
286
  this.eufy.on("close", () => this.onClose());
293
- this.eufy.on("device property changed", (device, name, value) => this.onDevicePropertyChanged(device, name, value));
294
- this.eufy.on("station command result", (station, result) => this.onStationCommandResult(station, result));
295
- this.eufy.on("station livestream start", (station, device, metadata, videostream, audiostream) => this.onStationLivestreamStart(station, device, metadata, videostream, audiostream));
296
- this.eufy.on("station livestream stop", (station, device) => this.onStationLivestreamStop(station, device));
297
- this.eufy.on("station rtsp url", (station, device, value) => this.onStationRTSPUrl(station, device, value));
298
- this.eufy.on("station property changed", (station, name, value) => this.onStationPropertyChanged(station, name, value));
287
+ this.eufy.on(
288
+ "device property changed",
289
+ (device, name, value) => this.onDevicePropertyChanged(device, name, value)
290
+ );
291
+ this.eufy.on(
292
+ "station command result",
293
+ (station, result) => this.onStationCommandResult(station, result)
294
+ );
295
+ this.eufy.on(
296
+ "station livestream start",
297
+ (station, device, metadata, videostream, audiostream) => this.onStationLivestreamStart(station, device, metadata, videostream, audiostream)
298
+ );
299
+ this.eufy.on(
300
+ "station livestream stop",
301
+ (station, device) => this.onStationLivestreamStop(station, device)
302
+ );
303
+ this.eufy.on(
304
+ "station rtsp url",
305
+ (station, device, value) => this.onStationRTSPUrl(station, device, value)
306
+ );
307
+ this.eufy.on(
308
+ "station property changed",
309
+ (station, name, value) => this.onStationPropertyChanged(station, name, value)
310
+ );
299
311
  this.eufy.on("station connect", (station) => this.onStationConnect(station));
300
312
  this.eufy.on("station close", (station) => this.onStationClose(station));
301
313
  this.eufy.on("tfa request", () => this.onTFARequest());
302
- this.eufy.on("captcha request", (captchaId, captcha) => this.onCaptchaRequest(captchaId, captcha));
314
+ this.eufy.on(
315
+ "captcha request",
316
+ (captchaId, captcha) => this.onCaptchaRequest(captchaId, captcha)
317
+ );
303
318
  this.eufy.setCameraMaxLivestreamDuration(this.config.maxLivestreamDuration);
304
319
  await this.eufy.connect();
305
320
  if (import_go2rtc_static.default) {
306
321
  const go2rtcConfig = {
307
- "api": {
308
- "listen": `:${this.config.go2rtc_api_port}`
322
+ api: {
323
+ listen: `:${this.config.go2rtc_api_port}`
309
324
  },
310
- "rtsp": {
311
- "listen": `:${this.config.go2rtc_rtsp_port}`
325
+ rtsp: {
326
+ listen: `:${this.config.go2rtc_rtsp_port}`
312
327
  },
313
- "srtp": {
314
- "listen": `:${this.config.go2rtc_srtp_port}`
328
+ srtp: {
329
+ listen: `:${this.config.go2rtc_srtp_port}`
315
330
  },
316
- "webrtc": {
317
- "listen": `:${this.config.go2rtc_webrtc_port}`
331
+ webrtc: {
332
+ listen: `:${this.config.go2rtc_webrtc_port}`
318
333
  },
319
- "ffmpeg": {
320
- // @ts-expect-error old code
321
- "bin": import_ffmpeg_for_homebridge.default !== "" && import_ffmpeg_for_homebridge.default !== void 0 ? import_ffmpeg_for_homebridge.default : "ffmpeg"
334
+ ffmpeg: {
335
+ bin: import_ffmpeg_for_homebridge.default !== "" && import_ffmpeg_for_homebridge.default !== void 0 ? import_ffmpeg_for_homebridge.default : "ffmpeg"
322
336
  },
323
- "streams": {}
337
+ streams: {}
324
338
  /*"log": {
325
339
  "level": "debug", // default level
326
340
  "api": "debug",
@@ -338,24 +352,7 @@ class euSec extends utils.Adapter {
338
352
  for (const device of await this.eufy.getDevices()) {
339
353
  go2rtcConfig.streams[device.getSerial()] = null;
340
354
  }
341
- const go2rtc = import_child_process.default.spawn(import_go2rtc_static.default, ["-config", JSON.stringify(go2rtcConfig)], { shell: false, detached: false, windowsHide: true });
342
- go2rtc.on("error", (error) => {
343
- this.log.error(`go2rtc error: ${error}`);
344
- });
345
- go2rtc.stdout.setEncoding("utf8");
346
- go2rtc.stdout.on("data", (data) => {
347
- this.log.info(`go2rtc started: ${data}`);
348
- });
349
- go2rtc.stderr.setEncoding("utf8");
350
- go2rtc.stderr.on("data", (data) => {
351
- this.log.error(`go2rtc error: ${data}`);
352
- });
353
- go2rtc.on("close", (exitcode) => {
354
- this.log.info(`go2rtc terminated with exitcode ${exitcode}`);
355
- });
356
- process.on("exit", () => {
357
- go2rtc.kill();
358
- });
355
+ this.startGo2rtc(JSON.stringify(go2rtcConfig));
359
356
  }
360
357
  }
361
358
  const channels = await this.getChannelsAsync();
@@ -369,6 +366,65 @@ class euSec extends utils.Adapter {
369
366
  }
370
367
  }
371
368
  }
369
+ /**
370
+ * Starts go2rtc and keeps it running. Without this, a crashed go2rtc was only reported with
371
+ * this.log.info() and never restarted, so every livestream failed until the adapter itself was
372
+ * restarted - at the default log level "info" the reason was not even visible.
373
+ *
374
+ * @param go2rtcConfig The serialized go2rtc configuration
375
+ */
376
+ startGo2rtc(go2rtcConfig) {
377
+ if (!import_go2rtc_static.default || this.terminating) {
378
+ return;
379
+ }
380
+ const startedAt = Date.now();
381
+ const go2rtc = import_node_child_process.default.spawn(import_go2rtc_static.default, ["-config", go2rtcConfig], {
382
+ shell: false,
383
+ detached: false,
384
+ windowsHide: true
385
+ });
386
+ this.go2rtcProcess = go2rtc;
387
+ go2rtc.on("error", (error) => {
388
+ this.log.error(`go2rtc error: ${error}`);
389
+ });
390
+ go2rtc.stdout.setEncoding("utf8");
391
+ go2rtc.stdout.on("data", (data) => {
392
+ this.log.info(`go2rtc: ${data}`);
393
+ });
394
+ go2rtc.stderr.setEncoding("utf8");
395
+ go2rtc.stderr.on("data", (data) => {
396
+ this.log.error(`go2rtc error: ${data}`);
397
+ });
398
+ go2rtc.on("close", (exitcode) => {
399
+ this.go2rtcProcess = void 0;
400
+ if (this.terminating) {
401
+ this.log.info(`go2rtc terminated with exitcode ${exitcode}`);
402
+ return;
403
+ }
404
+ if (Date.now() - startedAt >= GO2RTC_HEALTHY_RUNTIME) {
405
+ this.go2rtcRestarts = 0;
406
+ }
407
+ const delay = Math.min(GO2RTC_RESTART_DELAY_MAX, GO2RTC_RESTART_DELAY_MIN * 2 ** this.go2rtcRestarts);
408
+ this.go2rtcRestarts++;
409
+ this.log.error(
410
+ `go2rtc terminated unexpectedly with exitcode ${exitcode} - livestreaming is unavailable, restarting in ${delay / 1e3}s`
411
+ );
412
+ this.go2rtcRestartTimeout = this.setTimeout(() => {
413
+ this.go2rtcRestartTimeout = void 0;
414
+ this.startGo2rtc(go2rtcConfig);
415
+ }, delay);
416
+ });
417
+ }
418
+ stopGo2rtc() {
419
+ if (this.go2rtcRestartTimeout) {
420
+ this.clearTimeout(this.go2rtcRestartTimeout);
421
+ this.go2rtcRestartTimeout = void 0;
422
+ }
423
+ if (this.go2rtcProcess) {
424
+ this.go2rtcProcess.kill();
425
+ this.go2rtcProcess = void 0;
426
+ }
427
+ }
372
428
  async writePersistentData() {
373
429
  try {
374
430
  await this.writeFileAsync(this.namespace, this.persistentFile, JSON.stringify(this.persistentData));
@@ -382,14 +438,19 @@ class euSec extends utils.Adapter {
382
438
  });
383
439
  }
384
440
  /**
385
- * Is called when adapter shuts down - callback has to be called under any circumstances!
441
+ * Is called when the adapter shuts down - callback has to be called under any circumstances!
442
+ *
443
+ * @param callback
386
444
  */
387
445
  async onUnload(callback) {
388
446
  try {
447
+ this.terminating = true;
448
+ this.stopGo2rtc();
389
449
  await this.writePersistentData();
390
450
  if (this.eufy) {
391
- if (this.eufy.isConnected())
451
+ if (this.eufy.isConnected()) {
392
452
  await this.setStateAsync("info.connection", { val: false, ack: true }).catch();
453
+ }
393
454
  this.eufy.removeAllListeners();
394
455
  this.eufy.close();
395
456
  }
@@ -414,11 +475,16 @@ class euSec extends utils.Adapter {
414
475
  // }
415
476
  /**
416
477
  * Is called if a subscribed state changes
478
+ *
479
+ * @param id
480
+ * @param state
417
481
  */
418
482
  async onStateChange(id, state) {
419
483
  if (state) {
420
484
  if (!id || state.ack) {
421
- this.logger.debug(`state ${id} changed: ${state.val} (ack = ${state.ack}) was already acknowledged, ignore it...`);
485
+ this.logger.debug(
486
+ `state ${id} changed: ${state.val} (ack = ${state.ack}) was already acknowledged, ignore it...`
487
+ );
422
488
  return;
423
489
  }
424
490
  this.logger.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
@@ -459,13 +525,13 @@ class euSec extends utils.Adapter {
459
525
  const station = await this.eufy.getStation(station_sn);
460
526
  switch (station_state_name) {
461
527
  case import_types.StationStateID.REBOOT:
462
- await station.rebootHUB();
528
+ station.rebootHUB();
463
529
  break;
464
530
  case import_types.StationStateID.TRIGGER_ALARM_SOUND:
465
- await station.triggerStationAlarmSound(this.config.alarmSoundDuration);
531
+ station.triggerStationAlarmSound(this.config.alarmSoundDuration);
466
532
  break;
467
533
  case import_types.StationStateID.RESET_ALARM_SOUND:
468
- await station.resetStationAlarmSound();
534
+ station.resetStationAlarmSound();
469
535
  break;
470
536
  }
471
537
  }
@@ -479,9 +545,16 @@ class euSec extends utils.Adapter {
479
545
  if (obj) {
480
546
  if (obj.native.name !== void 0) {
481
547
  try {
482
- await this.eufy.setDeviceProperty(device_sn, obj.native.name, obj.common.type === "object" ? JSON.parse(state.val) : state.val);
548
+ await this.eufy.setDeviceProperty(
549
+ device_sn,
550
+ obj.native.name,
551
+ obj.common.type === "object" ? JSON.parse(state.val) : state.val
552
+ );
483
553
  } catch (error) {
484
- this.logger.error(`Error in setting property value (property: ${obj.native.name} value: ${state.val})`, error);
554
+ this.logger.error(
555
+ `Error in setting property value (property: ${obj.native.name} value: ${state.val})`,
556
+ error
557
+ );
485
558
  }
486
559
  return;
487
560
  }
@@ -497,44 +570,44 @@ class euSec extends utils.Adapter {
497
570
  await this.stopLivestream(device_sn);
498
571
  break;
499
572
  case import_types.DeviceStateID.TRIGGER_ALARM_SOUND:
500
- await station.triggerDeviceAlarmSound(device, this.config.alarmSoundDuration);
573
+ station.triggerDeviceAlarmSound(device, this.config.alarmSoundDuration);
501
574
  break;
502
575
  case import_types.DeviceStateID.RESET_ALARM_SOUND:
503
- await station.resetDeviceAlarmSound(device);
576
+ station.resetDeviceAlarmSound(device);
504
577
  break;
505
578
  case import_types.DeviceStateID.ROTATE_360:
506
- await station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.ROTATE360);
579
+ station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.ROTATE360);
507
580
  break;
508
581
  case import_types.DeviceStateID.PAN_LEFT:
509
- await station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.LEFT);
582
+ station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.LEFT);
510
583
  break;
511
584
  case import_types.DeviceStateID.PAN_RIGHT:
512
- await station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.RIGHT);
585
+ station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.RIGHT);
513
586
  break;
514
587
  case import_types.DeviceStateID.TILT_UP:
515
- await station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.UP);
588
+ station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.UP);
516
589
  break;
517
590
  case import_types.DeviceStateID.TILT_DOWN:
518
- await station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.DOWN);
591
+ station.panAndTilt(device, import_eufy_security_client.PanTiltDirection.DOWN);
519
592
  break;
520
593
  case import_types.DeviceStateID.CALIBRATE:
521
594
  if (device.isLock()) {
522
- await station.calibrateLock(device);
595
+ station.calibrateLock(device);
523
596
  } else {
524
- await station.calibrate(device);
597
+ station.calibrate(device);
525
598
  }
526
599
  break;
527
600
  case import_types.DeviceStateID.UNLOCK:
528
- await station.unlock(device);
601
+ station.unlock(device);
529
602
  break;
530
603
  case import_types.DeviceStateID.SET_DEFAULT_ANGLE:
531
- await station.setDefaultAngle(device);
604
+ station.setDefaultAngle(device);
532
605
  break;
533
606
  case import_types.DeviceStateID.SET_PRIVACY_ANGLE:
534
- await station.setPrivacyAngle(device);
607
+ station.setPrivacyAngle(device);
535
608
  break;
536
609
  case import_types.DeviceStateID.OPEN_BOX:
537
- await station.open(device);
610
+ station.open(device);
538
611
  break;
539
612
  }
540
613
  } catch (error) {
@@ -546,8 +619,10 @@ class euSec extends utils.Adapter {
546
619
  }
547
620
  }
548
621
  /**
549
- * Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...
550
- * Using this method requires "common.message" property to be set to true in io-package.json
622
+ * Some message was sent to this instance over the message box. Used by email, pushover, text2speech, ...
623
+ * Using this method requires the "common.message" property to be set to true in io-package.json
624
+ *
625
+ * @param obj
551
626
  */
552
627
  async onMessage(obj) {
553
628
  if (typeof obj === "object" && obj.message) {
@@ -559,58 +634,114 @@ class euSec extends utils.Adapter {
559
634
  const station = await this.eufy.getStation(obj.message.station_sn);
560
635
  const device = await this.eufy.getDevice(obj.message.device_sn);
561
636
  if (device.hasCommand(import_eufy_security_client.CommandName.DeviceQuickResponse)) {
562
- await station.quickResponse(device, obj.message.voice_id);
563
- if (obj.callback)
564
- this.sendTo(obj.from, obj.command, {
565
- sended: true,
566
- result: "quickResponse command sended"
567
- }, obj.callback);
637
+ station.quickResponse(device, obj.message.voice_id);
638
+ if (obj.callback) {
639
+ this.sendTo(
640
+ obj.from,
641
+ obj.command,
642
+ {
643
+ sended: true,
644
+ // because of back compatibility
645
+ sent: true,
646
+ result: "quickResponse command sent"
647
+ },
648
+ obj.callback
649
+ );
650
+ }
568
651
  } else {
569
- if (obj.callback)
570
- this.sendTo(obj.from, obj.command, {
571
- sended: false,
572
- result: "quickResponse command not supported by specified device"
573
- }, obj.callback);
652
+ if (obj.callback) {
653
+ this.sendTo(
654
+ obj.from,
655
+ obj.command,
656
+ {
657
+ sended: false,
658
+ // because of back compatibility
659
+ sent: false,
660
+ result: "quickResponse command not supported by specified device"
661
+ },
662
+ obj.callback
663
+ );
664
+ }
574
665
  }
575
666
  } catch (error) {
576
667
  if (error instanceof import_eufy_security_client.StationNotFoundError) {
577
- if (obj.callback)
578
- this.sendTo(obj.from, obj.command, {
579
- sended: false,
580
- result: "quickResponse command not sended because specified station doesn't exists"
581
- }, obj.callback);
668
+ if (obj.callback) {
669
+ this.sendTo(
670
+ obj.from,
671
+ obj.command,
672
+ {
673
+ sended: false,
674
+ // because of back compatibility
675
+ sent: false,
676
+ result: "quickResponse command not sent because specified station doesn't exists"
677
+ },
678
+ obj.callback
679
+ );
680
+ }
582
681
  } else if (error instanceof import_eufy_security_client.DeviceNotFoundError) {
583
- if (obj.callback)
584
- this.sendTo(obj.from, obj.command, {
585
- sended: false,
586
- result: "quickResponse command not sended because specified device doesn't exists"
587
- }, obj.callback);
682
+ if (obj.callback) {
683
+ this.sendTo(
684
+ obj.from,
685
+ obj.command,
686
+ {
687
+ sended: false,
688
+ // because of back compatibility
689
+ sent: false,
690
+ result: "quickResponse command not sent because specified device doesn't exists"
691
+ },
692
+ obj.callback
693
+ );
694
+ }
588
695
  } else {
589
696
  throw error;
590
697
  }
591
698
  }
592
699
  } else {
593
- if (obj.callback)
594
- this.sendTo(obj.from, obj.command, {
595
- sended: false,
596
- result: "quickResponse command not sended because some required parameters are missing"
597
- }, obj.callback);
700
+ if (obj.callback) {
701
+ this.sendTo(
702
+ obj.from,
703
+ obj.command,
704
+ {
705
+ sended: false,
706
+ // because of back compatibility
707
+ sent: false,
708
+ result: "quickResponse command not sent because some required parameters are missing"
709
+ },
710
+ obj.callback
711
+ );
712
+ }
598
713
  }
599
714
  } else if (obj.command === "getQuickResponseVoices") {
600
715
  this.log.debug(`getQuickResponseVoices command - message: ${JSON.stringify(obj.message)}`);
601
716
  if (typeof obj.message === "object" && typeof obj.message.device_sn === "string" && obj.message.device_sn !== "") {
602
717
  const voices = await this.eufy.getApi().getVoices(obj.message.device_sn);
603
- if (obj.callback)
604
- this.sendTo(obj.from, obj.command, {
605
- sended: true,
606
- result: voices
607
- }, obj.callback);
718
+ if (obj.callback) {
719
+ this.sendTo(
720
+ obj.from,
721
+ obj.command,
722
+ {
723
+ sended: true,
724
+ // because of back compatibility
725
+ sent: true,
726
+ result: voices
727
+ },
728
+ obj.callback
729
+ );
730
+ }
608
731
  } else {
609
- if (obj.callback)
610
- this.sendTo(obj.from, obj.command, {
611
- sended: false,
612
- result: "getQuickResponseVoices command not sended because some required parameters are missing"
613
- }, obj.callback);
732
+ if (obj.callback) {
733
+ this.sendTo(
734
+ obj.from,
735
+ obj.command,
736
+ {
737
+ sended: false,
738
+ // because of back compatibility
739
+ sent: false,
740
+ result: "getQuickResponseVoices command not sent because some required parameters are missing"
741
+ },
742
+ obj.callback
743
+ );
744
+ }
614
745
  }
615
746
  } else if (obj.command === "snooze") {
616
747
  this.log.debug(`snooze command - message: ${JSON.stringify(obj.message)}`);
@@ -619,47 +750,87 @@ class euSec extends utils.Adapter {
619
750
  const station = await this.eufy.getStation(obj.message.station_sn);
620
751
  const device = await this.eufy.getDevice(obj.message.device_sn);
621
752
  if (device.hasCommand(import_eufy_security_client.CommandName.DeviceSnooze)) {
622
- await station.snooze(device, {
753
+ station.snooze(device, {
623
754
  snooze_time: obj.message.snooze_time,
624
755
  snooze_chime: obj.message.snooze_chime,
625
756
  snooze_homebase: obj.message.snooze_homebase,
626
757
  snooze_motion: obj.message.snooze_motion
627
758
  });
628
- if (obj.callback)
629
- this.sendTo(obj.from, obj.command, {
630
- sended: true,
631
- result: "snooze command sended"
632
- }, obj.callback);
759
+ if (obj.callback) {
760
+ this.sendTo(
761
+ obj.from,
762
+ obj.command,
763
+ {
764
+ sended: true,
765
+ // because of back compatibility
766
+ sent: true,
767
+ result: "snooze command sent"
768
+ },
769
+ obj.callback
770
+ );
771
+ }
633
772
  } else {
634
- if (obj.callback)
635
- this.sendTo(obj.from, obj.command, {
636
- sended: false,
637
- result: "snooze command not supported by specified device"
638
- }, obj.callback);
773
+ if (obj.callback) {
774
+ this.sendTo(
775
+ obj.from,
776
+ obj.command,
777
+ {
778
+ sended: false,
779
+ // because of back compatibility
780
+ sent: false,
781
+ result: "snooze command not supported by specified device"
782
+ },
783
+ obj.callback
784
+ );
785
+ }
639
786
  }
640
787
  } catch (error) {
641
788
  if (error instanceof import_eufy_security_client.StationNotFoundError) {
642
- if (obj.callback)
643
- this.sendTo(obj.from, obj.command, {
644
- sended: false,
645
- result: "snooze command not sended because specified station doesn't exists"
646
- }, obj.callback);
789
+ if (obj.callback) {
790
+ this.sendTo(
791
+ obj.from,
792
+ obj.command,
793
+ {
794
+ sended: false,
795
+ // because of back compatibility
796
+ sent: false,
797
+ result: "snooze command not sent because specified station doesn't exists"
798
+ },
799
+ obj.callback
800
+ );
801
+ }
647
802
  } else if (error instanceof import_eufy_security_client.DeviceNotFoundError) {
648
- if (obj.callback)
649
- this.sendTo(obj.from, obj.command, {
650
- sended: false,
651
- result: "snooze command not sended because specified device doesn't exists"
652
- }, obj.callback);
803
+ if (obj.callback) {
804
+ this.sendTo(
805
+ obj.from,
806
+ obj.command,
807
+ {
808
+ sended: false,
809
+ // because of back compatibility
810
+ sent: false,
811
+ result: "snooze command not sent because specified device doesn't exists"
812
+ },
813
+ obj.callback
814
+ );
815
+ }
653
816
  } else {
654
817
  throw error;
655
818
  }
656
819
  }
657
820
  } else {
658
- if (obj.callback)
659
- this.sendTo(obj.from, obj.command, {
660
- sended: false,
661
- result: "snooze command not sended because some required parameters are missing"
662
- }, obj.callback);
821
+ if (obj.callback) {
822
+ this.sendTo(
823
+ obj.from,
824
+ obj.command,
825
+ {
826
+ sended: false,
827
+ // because of back compatibility
828
+ sent: false,
829
+ result: "snooze command not sent because some required parameters are missing"
830
+ },
831
+ obj.callback
832
+ );
833
+ }
663
834
  }
664
835
  } else if (obj.command === "chime") {
665
836
  this.log.debug(`snooze command - message: ${JSON.stringify(obj.message)}`);
@@ -667,28 +838,55 @@ class euSec extends utils.Adapter {
667
838
  try {
668
839
  const station = await this.eufy.getStation(obj.message.station_sn);
669
840
  if (station.hasCommand(import_eufy_security_client.CommandName.StationChime)) {
670
- await station.chimeHomebase(obj.message.ringtone !== void 0 && typeof obj.message.ringtone === "number" ? obj.message.ringtone : 0);
671
- if (obj.callback)
672
- this.sendTo(obj.from, obj.command, {
673
- sended: true,
674
- result: "chime command sended"
675
- }, obj.callback);
841
+ station.chimeHomebase(
842
+ obj.message.ringtone !== void 0 && typeof obj.message.ringtone === "number" ? obj.message.ringtone : 0
843
+ );
844
+ if (obj.callback) {
845
+ this.sendTo(
846
+ obj.from,
847
+ obj.command,
848
+ {
849
+ sended: true,
850
+ // because of back compatibility
851
+ sent: true,
852
+ result: "chime command sent"
853
+ },
854
+ obj.callback
855
+ );
856
+ }
676
857
  } else {
677
- if (obj.callback)
678
- this.sendTo(obj.from, obj.command, {
679
- sended: false,
680
- result: "chime command not supported by specified station"
681
- }, obj.callback);
858
+ if (obj.callback) {
859
+ this.sendTo(
860
+ obj.from,
861
+ obj.command,
862
+ {
863
+ sended: false,
864
+ // because of back compatibility
865
+ sent: false,
866
+ result: "chime command not supported by specified station"
867
+ },
868
+ obj.callback
869
+ );
870
+ }
871
+ }
872
+ if (obj.callback) {
873
+ this.sendTo(obj.from, obj.command, "chime command sent", obj.callback);
682
874
  }
683
- if (obj.callback)
684
- this.sendTo(obj.from, obj.command, "chime command sended", obj.callback);
685
875
  } catch (error) {
686
876
  if (error instanceof import_eufy_security_client.StationNotFoundError) {
687
- if (obj.callback)
688
- this.sendTo(obj.from, obj.command, {
689
- sended: false,
690
- result: "snooze command not sended because specified station doesn't exists"
691
- }, obj.callback);
877
+ if (obj.callback) {
878
+ this.sendTo(
879
+ obj.from,
880
+ obj.command,
881
+ {
882
+ sended: false,
883
+ // because of back compatibility
884
+ sent: false,
885
+ result: "snooze command not sent because specified station doesn't exists"
886
+ },
887
+ obj.callback
888
+ );
889
+ }
692
890
  } else {
693
891
  throw error;
694
892
  }
@@ -697,28 +895,52 @@ class euSec extends utils.Adapter {
697
895
  } else if (obj.command === "pollRefresh") {
698
896
  this.log.debug(`pollRefresh command`);
699
897
  await this.eufy.refreshCloudData();
700
- if (obj.callback)
701
- this.sendTo(obj.from, obj.command, {
702
- sended: true,
703
- result: "pollRefresh command sended"
704
- }, obj.callback);
898
+ if (obj.callback) {
899
+ this.sendTo(
900
+ obj.from,
901
+ obj.command,
902
+ {
903
+ sended: true,
904
+ // because of back compatibility
905
+ sent: true,
906
+ result: "pollRefresh command sent"
907
+ },
908
+ obj.callback
909
+ );
910
+ }
705
911
  } else {
706
912
  const errorMessage = `Received unknown message: ${JSON.stringify(obj.message)}`;
707
913
  this.log.warn(errorMessage);
708
- if (obj.callback)
709
- this.sendTo(obj.from, obj.command, {
710
- sended: false,
711
- result: errorMessage
712
- }, obj.callback);
914
+ if (obj.callback) {
915
+ this.sendTo(
916
+ obj.from,
917
+ obj.command,
918
+ {
919
+ sended: false,
920
+ // because of back compatibility
921
+ sent: false,
922
+ result: errorMessage
923
+ },
924
+ obj.callback
925
+ );
926
+ }
713
927
  }
714
928
  } catch (error) {
715
929
  const errorMessage = `Error during processing of received message: ${error instanceof Error ? `${error.name} - ${error.message}` : error}`;
716
930
  this.log.error(errorMessage);
717
- if (obj.callback)
718
- this.sendTo(obj.from, obj.command, {
719
- sended: false,
720
- result: errorMessage
721
- }, obj.callback);
931
+ if (obj.callback) {
932
+ this.sendTo(
933
+ obj.from,
934
+ obj.command,
935
+ {
936
+ sended: false,
937
+ // because of back compatibility
938
+ sent: false,
939
+ result: errorMessage
940
+ },
941
+ obj.callback
942
+ );
943
+ }
722
944
  }
723
945
  }
724
946
  }
@@ -772,7 +994,7 @@ class euSec extends utils.Adapter {
772
994
  obj.native.commandId = property.commandId;
773
995
  changed = true;
774
996
  }
775
- if (obj.common !== void 0 && !import_util.default.isDeepStrictEqual(obj.common, state)) {
997
+ if (obj.common !== void 0 && !import_node_util.default.isDeepStrictEqual(obj.common, state)) {
776
998
  changed = true;
777
999
  }
778
1000
  if (changed) {
@@ -795,8 +1017,13 @@ class euSec extends utils.Adapter {
795
1017
  });
796
1018
  }
797
1019
  const value = device.getPropertyValue(property.name);
798
- if (value !== void 0)
799
- await (0, import_utils.setStateChangedAsync)(this, id, (property.type === "string" || property.type === "object") && typeof value === "object" ? JSON.stringify(value) : value);
1020
+ if (value !== void 0) {
1021
+ await (0, import_utils.setStateChangedAsync)(
1022
+ this,
1023
+ id,
1024
+ (property.type === "string" || property.type === "object") && typeof value === "object" ? JSON.stringify(value) : value
1025
+ );
1026
+ }
800
1027
  }
801
1028
  }
802
1029
  async onDeviceAdded(device) {
@@ -817,8 +1044,9 @@ class euSec extends utils.Adapter {
817
1044
  });
818
1045
  const metadata = device.getPropertiesMetadata();
819
1046
  for (const property of Object.values(metadata)) {
820
- if (property.name !== import_eufy_security_client.PropertyName.DevicePicture)
821
- this.createAndSetState(device, property);
1047
+ if (property.name !== import_eufy_security_client.PropertyName.DevicePicture) {
1048
+ await this.createAndSetState(device, property);
1049
+ }
822
1050
  }
823
1051
  if (device.hasProperty(import_eufy_security_client.PropertyName.DevicePicture)) {
824
1052
  await this.setObjectNotExistsAsync(device.getStateID(import_types.DeviceStateID.PICTURE_URL), {
@@ -1063,11 +1291,16 @@ class euSec extends utils.Adapter {
1063
1291
  });
1064
1292
  }
1065
1293
  }
1066
- async onDeviceRemoved(device) {
1294
+ onDeviceRemoved(device) {
1067
1295
  this.delObjectAsync(device.getStateID("", 0), { recursive: true }).catch((error) => {
1068
1296
  this.logger.error(`Error deleting states of removed device`, error);
1069
1297
  });
1070
- (0, import_utils.removeFiles)(this, device.getStationSerial(), import_types.DataLocation.LAST_EVENT, device.getSerial()).catch((error) => {
1298
+ (0, import_utils.removeFiles)(
1299
+ this,
1300
+ device.getStationSerial(),
1301
+ import_types.DataLocation.LAST_EVENT,
1302
+ device.getSerial()
1303
+ ).catch((error) => {
1071
1304
  this.logger.error(`Error deleting fs contents of removed device`, error);
1072
1305
  });
1073
1306
  }
@@ -1101,7 +1334,7 @@ class euSec extends utils.Adapter {
1101
1334
  await this.setStateAsync(station.getStateID(import_types.StationStateID.CONNECTION), { val: false, ack: true });
1102
1335
  const metadata = station.getPropertiesMetadata();
1103
1336
  for (const property of Object.values(metadata)) {
1104
- this.createAndSetState(station, property);
1337
+ await this.createAndSetState(station, property);
1105
1338
  }
1106
1339
  if (station.hasCommand(import_eufy_security_client.CommandName.StationReboot)) {
1107
1340
  await this.setObjectNotExistsAsync(station.getStateID(import_types.StationStateID.REBOOT), {
@@ -1141,7 +1374,7 @@ class euSec extends utils.Adapter {
1141
1374
  });
1142
1375
  }
1143
1376
  }
1144
- async onStationRemoved(station) {
1377
+ onStationRemoved(station) {
1145
1378
  this.delObjectAsync(station.getStateID("", 0), { recursive: true }).catch((error) => {
1146
1379
  this.logger.error(`Error deleting states of removed station`, error);
1147
1380
  });
@@ -1177,7 +1410,7 @@ class euSec extends utils.Adapter {
1177
1410
  this.logger.error(`Device: ${device.getSerial()} - Error`, error);
1178
1411
  }
1179
1412
  }*/
1180
- async handlePushNotification(message) {
1413
+ handlePushNotification(message) {
1181
1414
  try {
1182
1415
  if (message.device_sn !== void 0) {
1183
1416
  }
@@ -1220,7 +1453,7 @@ class euSec extends utils.Adapter {
1220
1453
  }
1221
1454
  try {
1222
1455
  const allDevices = await this.getDevicesAsync();
1223
- const reg = new RegExp(`^${this.namespace}.[0-9A-Z]+$`);
1456
+ const reg = new RegExp(`^${this.namespace}\\.[0-9A-Z]+$`);
1224
1457
  for (const id of allDevices) {
1225
1458
  if (id._id.match(reg)) {
1226
1459
  const serial = id._id.replace(`${this.namespace}.`, "");
@@ -1234,7 +1467,7 @@ class euSec extends utils.Adapter {
1234
1467
  }
1235
1468
  try {
1236
1469
  const allDevices = await this.getDevicesAsync();
1237
- const reg = new RegExp(`^${this.namespace}.[0-9A-Z]+.[a-z]+.[0-9A-Z]+$`);
1470
+ const reg = new RegExp(`^${this.namespace}\\.[0-9A-Z]+\\.[a-z]+\\.[0-9A-Z]+$`);
1238
1471
  for (const id of allDevices) {
1239
1472
  if (id._id.match(reg)) {
1240
1473
  const values = id._id.split(".");
@@ -1263,24 +1496,28 @@ class euSec extends utils.Adapter {
1263
1496
  try {
1264
1497
  const device = await this.eufy.getDevice(deviceSerial);
1265
1498
  if (!device.hasProperty(object.native.name)) {
1266
- this.delObjectAsync(stateid);
1499
+ await this.delObjectAsync(stateid);
1267
1500
  }
1268
1501
  } catch (error) {
1269
1502
  if (error instanceof import_eufy_security_client.DeviceNotFoundError) {
1270
1503
  } else {
1271
- this.log.error(`Delete obsolete properties ERROR - device - ${JSON.stringify(error)}`);
1504
+ this.log.error(
1505
+ `Delete obsolete properties ERROR - device - ${JSON.stringify(error)}`
1506
+ );
1272
1507
  }
1273
1508
  }
1274
1509
  } else {
1275
1510
  try {
1276
1511
  const station = await this.eufy.getStation(stationSerial);
1277
1512
  if (!station.hasProperty(object.native.name)) {
1278
- this.delObjectAsync(stateid);
1513
+ await this.delObjectAsync(stateid);
1279
1514
  }
1280
1515
  } catch (error) {
1281
1516
  if (error instanceof import_eufy_security_client.StationNotFoundError) {
1282
1517
  } else {
1283
- this.log.error(`Delete obsolete properties ERROR - station - ${JSON.stringify(error)}`);
1518
+ this.log.error(
1519
+ `Delete obsolete properties ERROR - station - ${JSON.stringify(error)}`
1520
+ );
1284
1521
  }
1285
1522
  }
1286
1523
  }
@@ -1298,10 +1535,14 @@ class euSec extends utils.Adapter {
1298
1535
  await this.delFileAsync(this.namespace, content.file);
1299
1536
  } else {
1300
1537
  const dirContents = await this.readDirAsync(this.namespace, content.file);
1301
- for (const dir of dirContents.filter((fn) => Object.values(import_types.DataLocation).includes(fn.file) && fn.isDir)) {
1538
+ for (const dir of dirContents.filter(
1539
+ (fn) => Object.values(import_types.DataLocation).includes(fn.file) && fn.isDir
1540
+ )) {
1302
1541
  const files = await this.readDirAsync(this.namespace, path.join(content.file, dir.file));
1303
1542
  let deletedFiles = 0;
1304
- for (const file of files.filter((fn) => !deviceSerials.includes(fn.file.substring(0, 16)) && !fn.isDir)) {
1543
+ for (const file of files.filter(
1544
+ (fn) => !deviceSerials.includes(fn.file.substring(0, 16)) && !fn.isDir
1545
+ )) {
1305
1546
  await this.delFileAsync(this.namespace, path.join(content.file, dir.file, file.file));
1306
1547
  deletedFiles++;
1307
1548
  }
@@ -1405,11 +1646,15 @@ class euSec extends utils.Adapter {
1405
1646
  });
1406
1647
  await this.setStateAsync("info.mqtt_connection", { val: false, ack: true });
1407
1648
  }
1408
- async onStationCommandResult(station, result) {
1649
+ onStationCommandResult(station, result) {
1409
1650
  if (result.return_code !== 0 && result.command_type !== import_eufy_security_client.CommandType.P2P_QUERY_STATUS_IN_LOCK && result.command_type !== import_eufy_security_client.CommandType.CMD_STORAGE_INFO_HB3 && result.command_type !== import_eufy_security_client.CommandType.CMD_SDINFO_EX || result.return_code !== 0 && result.return_code !== import_eufy_security_client.ErrorCode.ERROR_DEV_BUSY && result.command_type === import_eufy_security_client.CommandType.CMD_STORAGE_INFO_HB3) {
1410
- this.logger.error(`Station: ${station.getSerial()} command ${import_eufy_security_client.CommandType[result.command_type]} failed with error: ${import_eufy_security_client.ErrorCode[result.return_code]} (${result.return_code})`);
1651
+ this.logger.error(
1652
+ `Station: ${station.getSerial()} command ${import_eufy_security_client.CommandType[result.command_type]} failed with error: ${import_eufy_security_client.ErrorCode[result.return_code]} (${result.return_code})`
1653
+ );
1411
1654
  } else if (result.return_code !== 0 && result.return_code !== import_eufy_security_client.TFCardStatus.REMOVE && result.return_code !== import_eufy_security_client.TFCardStatus.BUSY && result.command_type === import_eufy_security_client.CommandType.CMD_SDINFO_EX) {
1412
- this.logger.error(`Station: ${station.getSerial()} command ${import_eufy_security_client.CommandType[result.command_type]} failed with error: ${import_eufy_security_client.TFCardStatus[result.return_code]} (${result.return_code})`);
1655
+ this.logger.error(
1656
+ `Station: ${station.getSerial()} command ${import_eufy_security_client.CommandType[result.command_type]} failed with error: ${import_eufy_security_client.TFCardStatus[result.return_code]} (${result.return_code})`
1657
+ );
1413
1658
  }
1414
1659
  }
1415
1660
  async onStationPropertyChanged(station, name, value) {
@@ -1418,12 +1663,18 @@ class euSec extends utils.Adapter {
1418
1663
  const obj = await this.getObjectAsync(state);
1419
1664
  if (obj) {
1420
1665
  if (obj.native.name !== void 0 && obj.native.name === name) {
1421
- await (0, import_utils.setStateChangedAsync)(this, state, (obj.common.type === "string" || obj.common.type === "object") && typeof value === "object" ? JSON.stringify(value) : value);
1666
+ await (0, import_utils.setStateChangedAsync)(
1667
+ this,
1668
+ state,
1669
+ (obj.common.type === "string" || obj.common.type === "object") && typeof value === "object" ? JSON.stringify(value) : value
1670
+ );
1422
1671
  return;
1423
1672
  }
1424
1673
  }
1425
1674
  }
1426
- this.logger.debug(`onStationPropertyChanged(): Property "${name}" not implemented in this adapter (station: ${station.getSerial()} value: ${JSON.stringify(value)})`);
1675
+ this.logger.debug(
1676
+ `onStationPropertyChanged(): Property "${name}" not implemented in this adapter (station: ${station.getSerial()} value: ${JSON.stringify(value)})`
1677
+ );
1427
1678
  }
1428
1679
  async onDevicePropertyChanged(device, name, value) {
1429
1680
  const states = await this.getStatesAsync(`${device.getStateID("", 1)}.*`);
@@ -1431,11 +1682,15 @@ class euSec extends utils.Adapter {
1431
1682
  const obj = await this.getObjectAsync(state);
1432
1683
  if (obj) {
1433
1684
  if (obj.native.name !== void 0 && obj.native.name === name) {
1434
- await (0, import_utils.setStateChangedAsync)(this, state, (obj.common.type === "string" || obj.common.type === "object") && typeof value === "object" ? JSON.stringify(value) : value);
1685
+ await (0, import_utils.setStateChangedAsync)(
1686
+ this,
1687
+ state,
1688
+ (obj.common.type === "string" || obj.common.type === "object") && typeof value === "object" ? JSON.stringify(value) : value
1689
+ );
1435
1690
  switch (name) {
1436
1691
  case import_eufy_security_client.PropertyName.DeviceRTSPStream:
1437
1692
  if (value === false) {
1438
- this.delStateAsync(device.getStateID(import_types.DeviceStateID.RTSP_STREAM_URL));
1693
+ await this.delStateAsync(device.getStateID(import_types.DeviceStateID.RTSP_STREAM_URL));
1439
1694
  }
1440
1695
  break;
1441
1696
  }
@@ -1452,14 +1707,49 @@ class euSec extends utils.Adapter {
1452
1707
  await this.mkdirAsync(this.namespace, filePath);
1453
1708
  }
1454
1709
  await this.writeFileAsync(this.namespace, path.join(filePath, fileName), picture.data);
1455
- await this.setStateAsync(device.getStateID(import_types.DeviceStateID.PICTURE_URL), `/files/${this.namespace}/${device.getStationSerial()}/${import_types.DataLocation.LAST_EVENT}/${device.getSerial()}.${picture.type.ext}`, true);
1456
- await (0, import_utils.setStateChangedAsync)(this, device.getStateID(import_types.DeviceStateID.PICTURE_HTML), (0, import_utils.getImageAsHTML)(picture.data, picture.type.mime));
1710
+ await this.setStateAsync(
1711
+ device.getStateID(import_types.DeviceStateID.PICTURE_URL),
1712
+ `/files/${this.namespace}/${device.getStationSerial()}/${import_types.DataLocation.LAST_EVENT}/${device.getSerial()}.${picture.type.ext}`,
1713
+ true
1714
+ );
1715
+ await (0, import_utils.setStateChangedAsync)(
1716
+ this,
1717
+ device.getStateID(import_types.DeviceStateID.PICTURE_HTML),
1718
+ (0, import_utils.getImageAsHTML)(picture.data, picture.type.mime)
1719
+ );
1457
1720
  } catch (err) {
1458
1721
  const error = (0, import_eufy_security_client.ensureError)(err);
1459
1722
  this.logger.error("onDevicePropertyChanged - Property picture - Error", error);
1460
1723
  }
1461
1724
  } else {
1462
- this.logger.debug(`onDevicePropertyChanged(): Property "${name}" not implemented in this adapter (device: ${device.getSerial()} value: ${JSON.stringify(value)})`);
1725
+ this.logger.debug(
1726
+ `onDevicePropertyChanged(): Property "${name}" not implemented in this adapter (device: ${device.getSerial()} value: ${JSON.stringify(value)})`
1727
+ );
1728
+ }
1729
+ }
1730
+ /**
1731
+ * Tells whether a device streams at the "Auto" quality, where the camera is free to change the
1732
+ * resolution mid-stream. The numeric value of "Auto" is not the same for every device family,
1733
+ * so it is resolved through the state label of the property metadata.
1734
+ *
1735
+ * @param device The device to check
1736
+ * @returns true if the streaming quality is one of the "Auto" settings
1737
+ */
1738
+ isVideoStreamingQualityAuto(device) {
1739
+ var _a;
1740
+ if (!device.hasProperty(import_eufy_security_client.PropertyName.DeviceVideoStreamingQuality)) {
1741
+ return false;
1742
+ }
1743
+ try {
1744
+ const metadata = device.getPropertyMetadata(
1745
+ import_eufy_security_client.PropertyName.DeviceVideoStreamingQuality
1746
+ );
1747
+ const value = device.getPropertyValue(import_eufy_security_client.PropertyName.DeviceVideoStreamingQuality);
1748
+ const label = (_a = metadata == null ? void 0 : metadata.states) == null ? void 0 : _a[value];
1749
+ return typeof label === "string" && label.startsWith(VIDEO_STREAMING_QUALITY_AUTO_LABEL);
1750
+ } catch (error) {
1751
+ this.logger.debug(`Device: ${device.getSerial()} - Could not determine the video streaming quality`, error);
1752
+ return false;
1463
1753
  }
1464
1754
  }
1465
1755
  async startLivestream(device_sn) {
@@ -1468,12 +1758,21 @@ class euSec extends utils.Adapter {
1468
1758
  const station = await this.eufy.getStation(device.getStationSerial());
1469
1759
  if (station.isConnected() || station.isEnergySavingDevice()) {
1470
1760
  if (!station.isLiveStreaming(device)) {
1471
- this.eufy.startStationLivestream(device_sn);
1761
+ if (this.isVideoStreamingQualityAuto(device)) {
1762
+ this.logger.warn(
1763
+ `The video streaming quality of device ${device_sn} is set to "Auto". The camera may change the resolution while the stream is running, which browsers using MSE cannot follow (green picture or artifacts). Set a fixed quality if you see this.`
1764
+ );
1765
+ }
1766
+ await this.eufy.startStationLivestream(device_sn);
1472
1767
  } else {
1473
- this.logger.warn(`The stream for the device ${device_sn} cannot be started, because it is already streaming!`);
1768
+ this.logger.warn(
1769
+ `The stream for the device ${device_sn} cannot be started, because it is already streaming!`
1770
+ );
1474
1771
  }
1475
1772
  } else {
1476
- this.logger.warn(`The stream for the device ${device_sn} cannot be started, because there is no connection to station ${station.getSerial()}!`);
1773
+ this.logger.warn(
1774
+ `The stream for the device ${device_sn} cannot be started, because there is no connection to station ${station.getSerial()}!`
1775
+ );
1477
1776
  }
1478
1777
  } catch (error) {
1479
1778
  this.logger.error("Start livestream - Error", error);
@@ -1488,7 +1787,9 @@ class euSec extends utils.Adapter {
1488
1787
  if (await this.eufy.isStationConnected(device.getStationSerial()) && station.isLiveStreaming(camera)) {
1489
1788
  await this.eufy.stopStationLivestream(device_sn);
1490
1789
  } else {
1491
- this.logger.warn(`The stream for the device ${device_sn} cannot be stopped, because it isn't streaming!`);
1790
+ this.logger.warn(
1791
+ `The stream for the device ${device_sn} cannot be stopped, because it isn't streaming!`
1792
+ );
1492
1793
  }
1493
1794
  }
1494
1795
  } catch (error) {
@@ -1497,21 +1798,54 @@ class euSec extends utils.Adapter {
1497
1798
  }
1498
1799
  async onStationLivestreamStart(station, device, metadata, videostream, audiostream) {
1499
1800
  try {
1500
- this.setStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM), { val: `${this.config.https ? "https" : "http"}://${this.config.hostname}:${this.config.go2rtc_api_port}/stream.html?src=${device.getSerial()}`, ack: true });
1501
- this.setStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM_RTSP), { val: `rtsp://${this.config.hostname}:${this.config.go2rtc_rtsp_port}/${device.getSerial()}`, ack: true });
1502
- await (0, import_video.streamToGo2rtc)(device.getSerial(), videostream, audiostream, this.logger, this.config, this.namespace, metadata).catch((error) => {
1503
- this.logger.debug(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Stopping livestream...`, error);
1801
+ await this.setStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM), {
1802
+ val: `${this.config.https ? "https" : "http"}://${this.config.hostname}:${this.config.go2rtc_api_port}/stream.html?src=${device.getSerial()}`,
1803
+ ack: true
1504
1804
  });
1805
+ await this.setStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM_RTSP), {
1806
+ val: `rtsp://${this.config.hostname}:${this.config.go2rtc_rtsp_port}/${device.getSerial()}`,
1807
+ ack: true
1808
+ });
1809
+ const results = await (0, import_video.streamToGo2rtc)(
1810
+ device.getSerial(),
1811
+ videostream,
1812
+ audiostream,
1813
+ this.logger,
1814
+ this.config,
1815
+ this.namespace,
1816
+ metadata
1817
+ );
1818
+ if ((0, import_go2rtc.streamToGo2rtcFailed)(results)) {
1819
+ this.logger.warn(
1820
+ `Station: ${station.getSerial()} Device: ${device.getSerial()} - Streaming to go2rtc failed - Stopping livestream...`
1821
+ );
1822
+ await this.eufy.stopStationLivestream(device.getSerial()).catch((error) => {
1823
+ this.logger.error(
1824
+ `Station: ${station.getSerial()} Device: ${device.getSerial()} - Error during stopping livestream...`,
1825
+ error
1826
+ );
1827
+ });
1828
+ }
1505
1829
  } catch (error) {
1506
- this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Stopping livestream...`, error);
1507
- this.eufy.stopStationLivestream(device.getSerial()).catch(async (error2) => {
1508
- this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error during stopping livestream...`, error2);
1830
+ this.logger.error(
1831
+ `Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Stopping livestream...`,
1832
+ error
1833
+ );
1834
+ this.eufy.stopStationLivestream(device.getSerial()).catch((error2) => {
1835
+ this.logger.error(
1836
+ `Station: ${station.getSerial()} Device: ${device.getSerial()} - Error during stopping livestream...`,
1837
+ error2
1838
+ );
1509
1839
  });
1510
1840
  }
1511
1841
  }
1512
- onStationLivestreamStop(_station, device) {
1513
- this.delStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM));
1514
- this.delStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM_RTSP));
1842
+ async onStationLivestreamStop(_station, device) {
1843
+ try {
1844
+ await this.delStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM));
1845
+ await this.delStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM_RTSP));
1846
+ } catch (error) {
1847
+ this.log.error(`Cannot delete: ${error}`);
1848
+ }
1515
1849
  }
1516
1850
  /*private async onStationDownloadFinish(_station: Station, _device: Device): Promise<void> {
1517
1851
  //this.logger.trace(`Station: ${station.getSerial()} channel: ${channel}`);
@@ -1566,8 +1900,8 @@ class euSec extends utils.Adapter {
1566
1900
  await this.eufy.cancelStationDownload(device.getSerial());
1567
1901
  }
1568
1902
  }*/
1569
- onStationRTSPUrl(station, device, value) {
1570
- (0, import_utils.setStateChangedAsync)(this, device.getStateID(import_types.DeviceStateID.RTSP_STREAM_URL), value);
1903
+ async onStationRTSPUrl(station, device, value) {
1904
+ await (0, import_utils.setStateChangedAsync)(this, device.getStateID(import_types.DeviceStateID.RTSP_STREAM_URL), value);
1571
1905
  }
1572
1906
  async onStationConnect(station) {
1573
1907
  await this.setObjectNotExistsAsync(station.getStateID(import_types.StationStateID.CONNECTION), {
@@ -1596,16 +1930,30 @@ class euSec extends utils.Adapter {
1596
1930
  native: {}
1597
1931
  });
1598
1932
  await this.setStateAsync(station.getStateID(import_types.StationStateID.CONNECTION), { val: false, ack: true });
1933
+ try {
1934
+ for (const device of await this.eufy.getDevicesFromStation(station.getSerial())) {
1935
+ await this.delStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM)).catch(() => {
1936
+ });
1937
+ await this.delStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM_RTSP)).catch(() => {
1938
+ });
1939
+ }
1940
+ } catch (error) {
1941
+ this.logger.error(`Station: ${station.getSerial()} - Error while clearing livestream states`, error);
1942
+ }
1599
1943
  }
1600
1944
  onTFARequest() {
1601
- this.logger.warn(`Two factor authentication request received, please enter valid verification code in state ${this.namespace}.verify_code`);
1945
+ this.logger.warn(
1946
+ `Two factor authentication request received, please enter valid verification code in state ${this.namespace}.verify_code`
1947
+ );
1602
1948
  this.verify_code = true;
1603
1949
  }
1604
- onCaptchaRequest(captchaId, captcha) {
1950
+ async onCaptchaRequest(captchaId, captcha) {
1605
1951
  this.captchaId = captchaId;
1606
- this.logger.warn(`Captcha authentication request received, please enter valid captcha in state ${this.namespace}.captcha`);
1952
+ this.logger.warn(
1953
+ `Captcha authentication request received, please enter valid captcha in state ${this.namespace}.captcha`
1954
+ );
1607
1955
  this.logger.warn(`Captcha: <img src="${captcha}">`);
1608
- this.setStateAsync("received_captcha_html", { val: `<img src="${captcha}">`, ack: true });
1956
+ await this.setStateAsync("received_captcha_html", { val: `<img src="${captcha}">`, ack: true });
1609
1957
  }
1610
1958
  }
1611
1959
  if (require.main !== module) {