iobroker.eusec 1.2.0 → 1.3.0

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
@@ -5,6 +5,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
8
12
  var __copyProps = (to, from, except, desc) => {
9
13
  if (from && typeof from === "object" || typeof from === "function") {
10
14
  for (let key of __getOwnPropNames(from))
@@ -14,43 +18,65 @@ var __copyProps = (to, from, except, desc) => {
14
18
  return to;
15
19
  };
16
20
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
17
25
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
18
26
  mod
19
27
  ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var main_exports = {};
30
+ __export(main_exports, {
31
+ euSec: () => euSec
32
+ });
33
+ module.exports = __toCommonJS(main_exports);
20
34
  var utils = __toESM(require("@iobroker/adapter-core"));
21
35
  var path = __toESM(require("path"));
22
36
  var import_eufy_security_client = require("eufy-security-client");
23
37
  var import_i18n_iso_countries = require("i18n-iso-countries");
24
38
  var import_i18n_iso_languages = require("@cospired/i18n-iso-languages");
25
- var import_fs_extra = __toESM(require("fs-extra"));
26
39
  var import_util = __toESM(require("util"));
27
40
  var import_child_process = __toESM(require("child_process"));
28
41
  var import_go2rtc_static = __toESM(require("go2rtc-static"));
29
- var import_os = __toESM(require("os"));
42
+ var import_ffmpeg_for_homebridge = __toESM(require("ffmpeg-for-homebridge"));
30
43
  var import_types = require("./lib/types");
31
44
  var import_utils = require("./lib/utils");
32
45
  var import_log = require("./lib/log");
33
46
  var import_video = require("./lib/video");
34
47
  class euSec extends utils.Adapter {
48
+ eufy;
49
+ /*private downloadEvent: {
50
+ [index: string]: NodeJS.Timeout;
51
+ } = {};*/
52
+ persistentFile = "adapter.json";
53
+ persistentDriverFile = "driver.json";
54
+ logger;
55
+ persistentData = {
56
+ version: ""
57
+ };
58
+ captchaId = null;
59
+ verify_code = false;
60
+ skipInit = false;
35
61
  constructor(options = {}) {
36
62
  super({
37
63
  ...options,
38
64
  name: "eusec"
39
65
  });
40
- this.persistentData = {
41
- version: ""
42
- };
43
- this.captchaId = null;
44
- this.verify_code = false;
45
- const data_dir = utils.getAbsoluteInstanceDataDir(this);
46
- this.persistentFile = path.join(data_dir, "adapter.json");
47
- if (!import_fs_extra.default.existsSync(data_dir))
48
- import_fs_extra.default.mkdirSync(data_dir);
49
66
  this.on("ready", this.onReady.bind(this));
50
67
  this.on("stateChange", this.onStateChange.bind(this));
68
+ this.on("message", this.onMessage.bind(this));
51
69
  this.on("unload", this.onUnload.bind(this));
52
70
  }
71
+ restartAdapter() {
72
+ this.skipInit = true;
73
+ this.restart();
74
+ }
75
+ /**
76
+ * Is called when databases are connected and adapter received configuration.
77
+ */
53
78
  async onReady() {
79
+ var _a;
54
80
  this.logger = new import_log.ioBrokerLogger(this.log);
55
81
  await this.setObjectNotExistsAsync("verify_code", {
56
82
  type: "state",
@@ -157,132 +183,209 @@ class euSec extends utils.Adapter {
157
183
  this.logger.error("Reset sensor states - Error", error);
158
184
  }
159
185
  try {
160
- if (import_fs_extra.default.statSync(this.persistentFile).isFile()) {
161
- const fileContent = import_fs_extra.default.readFileSync(this.persistentFile, "utf8");
162
- this.persistentData = JSON.parse(fileContent);
186
+ if (await this.fileExistsAsync(this.namespace, this.persistentFile)) {
187
+ const fileContent = await this.readFileAsync(this.namespace, this.persistentFile);
188
+ this.persistentData = JSON.parse(fileContent.file.toString("utf8"));
163
189
  }
164
190
  } catch (error) {
165
- this.logger.debug("No stored data from last exit found.", error);
166
- }
167
- this.subscribeStates("verify_code");
168
- this.subscribeStates("captcha");
169
- const systemConfig = await this.getForeignObjectAsync("system.config");
170
- let countryCode = void 0;
171
- let languageCode = void 0;
172
- if (systemConfig) {
173
- countryCode = (0, import_i18n_iso_countries.getAlpha2Code)(systemConfig.common.country, "en");
174
- if ((0, import_i18n_iso_languages.isValid)(systemConfig.common.language))
175
- languageCode = systemConfig.common.language;
176
- }
177
- if (this.config.hostname === "") {
178
- this.config.hostname = import_os.default.hostname();
191
+ this.logger.debug("No adapter stored data from last exit found.", error);
179
192
  }
193
+ let persistentDriverData = "{}";
180
194
  try {
181
- if (this.persistentData.version !== this.version) {
182
- const currentVersion = Number.parseFloat((0, import_utils.removeLastChar)(this.version, "."));
183
- const previousVersion = this.persistentData.version !== "" && this.persistentData.version !== void 0 ? Number.parseFloat((0, import_utils.removeLastChar)(this.persistentData.version, ".")) : 0;
184
- this.logger.debug(`Handling of adapter update - currentVersion: ${currentVersion} previousVersion: ${previousVersion}`);
185
- if (previousVersion < currentVersion) {
186
- await (0, import_utils.handleUpdate)(this, this.logger, previousVersion);
187
- this.persistentData.version = this.version;
188
- this.writePersistentData();
189
- }
195
+ if (await this.fileExistsAsync(this.namespace, this.persistentDriverFile)) {
196
+ const fileContent = await this.readFileAsync(this.namespace, this.persistentDriverFile);
197
+ persistentDriverData = fileContent.file.toString("utf8");
190
198
  }
191
199
  } catch (error) {
192
- this.logger.error(`Handling of adapter update - Error:`, error);
200
+ this.logger.debug("No driver stored data from last exit found.", error);
193
201
  }
194
- let connectionType = import_eufy_security_client.P2PConnectionType.QUICKEST;
195
- if (this.config.p2pConnectionType === "only_local") {
196
- connectionType = import_eufy_security_client.P2PConnectionType.ONLY_LOCAL;
202
+ this.subscribeStates("verify_code");
203
+ this.subscribeStates("captcha");
204
+ const hosts = await this.getForeignObjectsAsync("system.host.*", "host");
205
+ if (hosts !== void 0 && hosts !== null && Object.values(hosts).length !== 0) {
206
+ if (this.config.hostname === "") {
207
+ this.config.hostname = Object.values(hosts)[0].native.os.hostname;
208
+ }
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
+ case 18:
214
+ fixNeeded = nodeVersion.localeCompare("18.19.1", void 0, { numeric: true, sensitivity: "base" });
215
+ break;
216
+ case 20:
217
+ fixNeeded = nodeVersion.localeCompare("20.11.1", void 0, { numeric: true, sensitivity: "base" });
218
+ break;
219
+ default:
220
+ fixNeeded = nodeVersion.localeCompare("21.6.2", void 0, { numeric: true, sensitivity: "base" });
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
+ }
197
234
  }
198
- const config = {
199
- username: this.config.username,
200
- password: this.config.password,
201
- country: countryCode,
202
- language: languageCode,
203
- persistentDir: utils.getAbsoluteInstanceDataDir(this),
204
- eventDurationSeconds: this.config.eventDuration,
205
- p2pConnectionSetup: connectionType,
206
- pollingIntervalMinutes: this.config.pollingInterval,
207
- acceptInvitations: this.config.acceptInvitations
208
- };
209
- this.eufy = await import_eufy_security_client.EufySecurity.initialize(config, this.logger);
210
- this.eufy.on("station added", (station) => this.onStationAdded(station));
211
- this.eufy.on("device added", (device) => this.onDeviceAdded(device));
212
- this.eufy.on("station removed", (station) => this.onStationRemoved(station));
213
- this.eufy.on("device removed", (device) => this.onDeviceRemoved(device));
214
- this.eufy.on("push message", (messages) => this.handlePushNotification(messages));
215
- this.eufy.on("push connect", () => this.onPushConnect());
216
- this.eufy.on("push close", () => this.onPushClose());
217
- this.eufy.on("mqtt connect", () => this.onMQTTConnect());
218
- this.eufy.on("mqtt close", () => this.onMQTTClose());
219
- this.eufy.on("connect", () => this.onConnect());
220
- this.eufy.on("close", () => this.onClose());
221
- this.eufy.on("device property changed", (device, name, value) => this.onDevicePropertyChanged(device, name, value));
222
- this.eufy.on("station command result", (station, result) => this.onStationCommandResult(station, result));
223
- this.eufy.on("station livestream start", (station, device, metadata, videostream, audiostream) => this.onStationLivestreamStart(station, device, metadata, videostream, audiostream));
224
- this.eufy.on("station livestream stop", (station, device) => this.onStationLivestreamStop(station, device));
225
- this.eufy.on("station rtsp url", (station, device, value) => this.onStationRTSPUrl(station, device, value));
226
- this.eufy.on("station property changed", (station, name, value) => this.onStationPropertyChanged(station, name, value));
227
- this.eufy.on("station connect", (station) => this.onStationConnect(station));
228
- this.eufy.on("station close", (station) => this.onStationClose(station));
229
- this.eufy.on("tfa request", () => this.onTFARequest());
230
- this.eufy.on("captcha request", (captchaId, captcha) => this.onCaptchaRequest(captchaId, captcha));
231
- this.eufy.setCameraMaxLivestreamDuration(this.config.maxLivestreamDuration);
232
- await this.eufy.connect();
233
- if (import_go2rtc_static.default) {
234
- const go2rtcConfig = {
235
- "api": {
236
- "listen": `:${this.config.go2rtc_api_port}`
237
- },
238
- "rtsp": {
239
- "listen": `:${this.config.go2rtc_rtsp_port}`
240
- },
241
- "srtp": {
242
- "listen": `:${this.config.go2rtc_srtp_port}`
243
- },
244
- "webrtc": {
245
- "listen": `:${this.config.go2rtc_webrtc_port}`
246
- },
247
- "streams": {}
248
- };
249
- if (this.config.go2rtc_rtsp_username !== "" && this.config.go2rtc_rtsp_password !== "") {
250
- go2rtcConfig.rtsp.username = this.config.go2rtc_rtsp_username;
251
- go2rtcConfig.rtsp.password = this.config.go2rtc_rtsp_password;
235
+ if (!this.skipInit) {
236
+ const systemConfig = await this.getForeignObjectAsync("system.config");
237
+ let countryCode = void 0;
238
+ let languageCode = void 0;
239
+ if (systemConfig) {
240
+ countryCode = (0, import_i18n_iso_countries.getAlpha2Code)(systemConfig.common.country, "en");
241
+ if ((0, import_i18n_iso_languages.isValid)(systemConfig.common.language))
242
+ languageCode = systemConfig.common.language;
252
243
  }
253
- for (const device of await this.eufy.getDevices()) {
254
- go2rtcConfig.streams[device.getSerial()] = null;
244
+ if (this.config.country !== "iobroker") {
245
+ countryCode = this.config.country;
246
+ }
247
+ try {
248
+ if (this.persistentData.version !== this.version) {
249
+ const currentVersion = Number.parseFloat((0, import_utils.removeLastChar)(this.version, "."));
250
+ 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}`);
252
+ if (previousVersion < currentVersion) {
253
+ await (0, import_utils.handleUpdate)(this, this.logger, previousVersion, currentVersion);
254
+ this.persistentData.version = this.version;
255
+ await this.writePersistentData();
256
+ }
257
+ }
258
+ } catch (error) {
259
+ this.logger.error(`Handling of adapter update - Error:`, error);
260
+ }
261
+ let connectionType = import_eufy_security_client.P2PConnectionType.QUICKEST;
262
+ if (this.config.p2pConnectionType === "only_local") {
263
+ connectionType = import_eufy_security_client.P2PConnectionType.ONLY_LOCAL;
264
+ }
265
+ if (this.config.username !== "" && this.config.password !== "") {
266
+ const config = {
267
+ username: this.config.username,
268
+ password: this.config.password,
269
+ country: countryCode,
270
+ language: languageCode,
271
+ persistentData: persistentDriverData,
272
+ eventDurationSeconds: this.config.eventDuration,
273
+ p2pConnectionSetup: connectionType,
274
+ pollingIntervalMinutes: this.config.pollingInterval,
275
+ acceptInvitations: this.config.acceptInvitations,
276
+ logging: {
277
+ 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
+ }
279
+ };
280
+ this.eufy = await import_eufy_security_client.EufySecurity.initialize(config, this.logger);
281
+ this.eufy.on("persistent data", (data) => this.onPersistentData(data));
282
+ this.eufy.on("station added", (station) => this.onStationAdded(station));
283
+ this.eufy.on("device added", (device) => this.onDeviceAdded(device));
284
+ this.eufy.on("station removed", (station) => this.onStationRemoved(station));
285
+ this.eufy.on("device removed", (device) => this.onDeviceRemoved(device));
286
+ this.eufy.on("push message", (messages) => this.handlePushNotification(messages));
287
+ this.eufy.on("push connect", () => this.onPushConnect());
288
+ this.eufy.on("push close", () => this.onPushClose());
289
+ this.eufy.on("mqtt connect", () => this.onMQTTConnect());
290
+ this.eufy.on("mqtt close", () => this.onMQTTClose());
291
+ this.eufy.on("connect", () => this.onConnect());
292
+ 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));
299
+ this.eufy.on("station connect", (station) => this.onStationConnect(station));
300
+ this.eufy.on("station close", (station) => this.onStationClose(station));
301
+ this.eufy.on("tfa request", () => this.onTFARequest());
302
+ this.eufy.on("captcha request", (captchaId, captcha) => this.onCaptchaRequest(captchaId, captcha));
303
+ this.eufy.setCameraMaxLivestreamDuration(this.config.maxLivestreamDuration);
304
+ await this.eufy.connect();
305
+ if (import_go2rtc_static.default) {
306
+ const go2rtcConfig = {
307
+ "api": {
308
+ "listen": `:${this.config.go2rtc_api_port}`
309
+ },
310
+ "rtsp": {
311
+ "listen": `:${this.config.go2rtc_rtsp_port}`
312
+ },
313
+ "srtp": {
314
+ "listen": `:${this.config.go2rtc_srtp_port}`
315
+ },
316
+ "webrtc": {
317
+ "listen": `:${this.config.go2rtc_webrtc_port}`
318
+ },
319
+ "ffmpeg": {
320
+ "bin": import_ffmpeg_for_homebridge.default !== "" && import_ffmpeg_for_homebridge.default !== void 0 ? import_ffmpeg_for_homebridge.default : "ffmpeg"
321
+ },
322
+ "streams": {}
323
+ /*"log": {
324
+ "level": "debug", // default level
325
+ "api": "debug",
326
+ "exec": "debug",
327
+ "ngrok": "debug",
328
+ "rtsp": "debug",
329
+ "streams": "debug",
330
+ "webrtc": "debug",
331
+ }*/
332
+ };
333
+ if (this.config.go2rtc_rtsp_username !== "" && this.config.go2rtc_rtsp_password !== "") {
334
+ go2rtcConfig.rtsp.username = this.config.go2rtc_rtsp_username;
335
+ go2rtcConfig.rtsp.password = this.config.go2rtc_rtsp_password;
336
+ }
337
+ for (const device of await this.eufy.getDevices()) {
338
+ go2rtcConfig.streams[device.getSerial()] = null;
339
+ }
340
+ const go2rtc = import_child_process.default.spawn(import_go2rtc_static.default, ["-config", JSON.stringify(go2rtcConfig)], { shell: false, detached: false, windowsHide: true });
341
+ go2rtc.on("error", (error) => {
342
+ this.log.error(`go2rtc error: ${error}`);
343
+ });
344
+ go2rtc.stdout.setEncoding("utf8");
345
+ go2rtc.stdout.on("data", (data) => {
346
+ this.log.info(`go2rtc started: ${data}`);
347
+ });
348
+ go2rtc.stderr.setEncoding("utf8");
349
+ go2rtc.stderr.on("data", (data) => {
350
+ this.log.error(`go2rtc error: ${data}`);
351
+ });
352
+ go2rtc.on("close", (exitcode) => {
353
+ this.log.info(`go2rtc terminated with exitcode ${exitcode}`);
354
+ });
355
+ process.on("exit", () => {
356
+ go2rtc.kill();
357
+ });
358
+ }
359
+ }
360
+ const channels = await this.getChannelsAsync();
361
+ for (const channel of channels) {
362
+ if (channel.common.name === "unknown") {
363
+ const states = await this.getStatesAsync(`${channel._id}.*`);
364
+ if (Object.keys(states).length === 0) {
365
+ await this.delObjectAsync(channel._id);
366
+ }
367
+ }
255
368
  }
256
- const go2rtc = import_child_process.default.spawn(import_go2rtc_static.default, ["-config", JSON.stringify(go2rtcConfig)], { shell: false, detached: false, windowsHide: true });
257
- go2rtc.on("error", (error) => {
258
- this.log.error(`go2rtc error: ${error}`);
259
- });
260
- go2rtc.stdout.setEncoding("utf8");
261
- go2rtc.stdout.on("data", (data) => {
262
- this.log.info(`go2rtc started: ${data}`);
263
- });
264
- go2rtc.stderr.setEncoding("utf8");
265
- go2rtc.stderr.on("data", (data) => {
266
- this.log.error(`go2rtc error: ${data}`);
267
- });
268
- go2rtc.on("close", (exitcode) => {
269
- this.log.info(`go2rtc terminated with exitcode ${exitcode}`);
270
- });
271
- process.on("exit", () => {
272
- go2rtc.kill();
273
- });
274
369
  }
275
370
  }
276
- writePersistentData() {
371
+ async writePersistentData() {
277
372
  try {
278
- import_fs_extra.default.writeFileSync(this.persistentFile, JSON.stringify(this.persistentData));
373
+ await this.writeFileAsync(this.namespace, this.persistentFile, JSON.stringify(this.persistentData));
279
374
  } catch (error) {
280
375
  this.logger.error(`writePersistentData() - Error: ${error}`);
281
376
  }
282
377
  }
378
+ onPersistentData(data) {
379
+ this.writeFileAsync(this.namespace, this.persistentDriverFile, data).catch((error) => {
380
+ this.logger.error(`writePersistentDriverData() - Error: ${error}`);
381
+ });
382
+ }
383
+ /**
384
+ * Is called when adapter shuts down - callback has to be called under any circumstances!
385
+ */
283
386
  async onUnload(callback) {
284
387
  try {
285
- this.writePersistentData();
388
+ await this.writePersistentData();
286
389
  if (this.eufy) {
287
390
  if (this.eufy.isConnected())
288
391
  await this.setStateAsync("info.connection", { val: false, ack: true }).catch();
@@ -294,6 +397,23 @@ class euSec extends utils.Adapter {
294
397
  callback();
295
398
  }
296
399
  }
400
+ // If you need to react to object changes, uncomment the following block and the corresponding line in the constructor.
401
+ // You also need to subscribe to the objects with `this.subscribeObjects`, similar to `this.subscribeStates`.
402
+ // /**
403
+ // * Is called if a subscribed object changes
404
+ // */
405
+ // private onObjectChange(id: string, obj: ioBroker.Object | null | undefined): void {
406
+ // if (obj) {
407
+ // // The object was changed
408
+ // this.log.info(`object ${id} changed: ${JSON.stringify(obj)}`);
409
+ // } else {
410
+ // // The object was deleted
411
+ // this.log.info(`object ${id} deleted`);
412
+ // }
413
+ // }
414
+ /**
415
+ * Is called if a subscribed state changes
416
+ */
297
417
  async onStateChange(id, state) {
298
418
  if (state) {
299
419
  if (!id || state.ack) {
@@ -412,6 +532,9 @@ class euSec extends utils.Adapter {
412
532
  case import_types.DeviceStateID.SET_PRIVACY_ANGLE:
413
533
  await station.setPrivacyAngle(device);
414
534
  break;
535
+ case import_types.DeviceStateID.OPEN_BOX:
536
+ await station.open(device);
537
+ break;
415
538
  }
416
539
  } catch (error) {
417
540
  this.logger.error(`cameras - Error:`, error);
@@ -421,6 +544,183 @@ class euSec extends utils.Adapter {
421
544
  this.logger.debug(`state ${id} deleted`);
422
545
  }
423
546
  }
547
+ /**
548
+ * Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...
549
+ * Using this method requires "common.message" property to be set to true in io-package.json
550
+ */
551
+ async onMessage(obj) {
552
+ if (typeof obj === "object" && obj.message) {
553
+ try {
554
+ if (obj.command === "quickResponse") {
555
+ this.log.debug(`quickResponse command - message: ${JSON.stringify(obj.message)}`);
556
+ if (typeof obj.message === "object" && typeof obj.message.station_sn === "string" && obj.message.station_sn !== "" && typeof obj.message.device_sn === "string" && obj.message.device_sn !== "" && typeof obj.message.voice_id === "number") {
557
+ try {
558
+ const station = await this.eufy.getStation(obj.message.station_sn);
559
+ const device = await this.eufy.getDevice(obj.message.device_sn);
560
+ if (device.hasCommand(import_eufy_security_client.CommandName.DeviceQuickResponse)) {
561
+ await station.quickResponse(device, obj.message.voice_id);
562
+ if (obj.callback)
563
+ this.sendTo(obj.from, obj.command, {
564
+ sended: true,
565
+ result: "quickResponse command sended"
566
+ }, obj.callback);
567
+ } else {
568
+ if (obj.callback)
569
+ this.sendTo(obj.from, obj.command, {
570
+ sended: false,
571
+ result: "quickResponse command not supported by specified device"
572
+ }, obj.callback);
573
+ }
574
+ } catch (error) {
575
+ if (error instanceof import_eufy_security_client.StationNotFoundError) {
576
+ if (obj.callback)
577
+ this.sendTo(obj.from, obj.command, {
578
+ sended: false,
579
+ result: "quickResponse command not sended because specified station doesn't exists"
580
+ }, obj.callback);
581
+ } else if (error instanceof import_eufy_security_client.DeviceNotFoundError) {
582
+ if (obj.callback)
583
+ this.sendTo(obj.from, obj.command, {
584
+ sended: false,
585
+ result: "quickResponse command not sended because specified device doesn't exists"
586
+ }, obj.callback);
587
+ } else {
588
+ throw error;
589
+ }
590
+ }
591
+ } else {
592
+ if (obj.callback)
593
+ this.sendTo(obj.from, obj.command, {
594
+ sended: false,
595
+ result: "quickResponse command not sended because some required parameters are missing"
596
+ }, obj.callback);
597
+ }
598
+ } else if (obj.command === "getQuickResponseVoices") {
599
+ this.log.debug(`getQuickResponseVoices command - message: ${JSON.stringify(obj.message)}`);
600
+ if (typeof obj.message === "object" && typeof obj.message.device_sn === "string" && obj.message.device_sn !== "") {
601
+ const voices = await this.eufy.getApi().getVoices(obj.message.device_sn);
602
+ if (obj.callback)
603
+ this.sendTo(obj.from, obj.command, {
604
+ sended: true,
605
+ result: voices
606
+ }, obj.callback);
607
+ } else {
608
+ if (obj.callback)
609
+ this.sendTo(obj.from, obj.command, {
610
+ sended: false,
611
+ result: "getQuickResponseVoices command not sended because some required parameters are missing"
612
+ }, obj.callback);
613
+ }
614
+ } else if (obj.command === "snooze") {
615
+ this.log.debug(`snooze command - message: ${JSON.stringify(obj.message)}`);
616
+ if (typeof obj.message === "object" && typeof obj.message.station_sn === "string" && obj.message.station_sn !== "" && typeof obj.message.device_sn === "string" && obj.message.device_sn !== "" && typeof obj.message.snooze_time === "number" && (obj.message.snooze_chime === void 0 || typeof obj.message.snooze_chime === "boolean") && (obj.message.snooze_homebase === void 0 || typeof obj.message.snooze_homebase === "boolean") && (obj.message.snooze_motion === void 0 || typeof obj.message.snooze_motion === "boolean")) {
617
+ try {
618
+ const station = await this.eufy.getStation(obj.message.station_sn);
619
+ const device = await this.eufy.getDevice(obj.message.device_sn);
620
+ if (device.hasCommand(import_eufy_security_client.CommandName.DeviceSnooze)) {
621
+ await station.snooze(device, {
622
+ snooze_time: obj.message.snooze_time,
623
+ snooze_chime: obj.message.snooze_chime,
624
+ snooze_homebase: obj.message.snooze_homebase,
625
+ snooze_motion: obj.message.snooze_motion
626
+ });
627
+ if (obj.callback)
628
+ this.sendTo(obj.from, obj.command, {
629
+ sended: true,
630
+ result: "snooze command sended"
631
+ }, obj.callback);
632
+ } else {
633
+ if (obj.callback)
634
+ this.sendTo(obj.from, obj.command, {
635
+ sended: false,
636
+ result: "snooze command not supported by specified device"
637
+ }, obj.callback);
638
+ }
639
+ } catch (error) {
640
+ if (error instanceof import_eufy_security_client.StationNotFoundError) {
641
+ if (obj.callback)
642
+ this.sendTo(obj.from, obj.command, {
643
+ sended: false,
644
+ result: "snooze command not sended because specified station doesn't exists"
645
+ }, obj.callback);
646
+ } else if (error instanceof import_eufy_security_client.DeviceNotFoundError) {
647
+ if (obj.callback)
648
+ this.sendTo(obj.from, obj.command, {
649
+ sended: false,
650
+ result: "snooze command not sended because specified device doesn't exists"
651
+ }, obj.callback);
652
+ } else {
653
+ throw error;
654
+ }
655
+ }
656
+ } else {
657
+ if (obj.callback)
658
+ this.sendTo(obj.from, obj.command, {
659
+ sended: false,
660
+ result: "snooze command not sended because some required parameters are missing"
661
+ }, obj.callback);
662
+ }
663
+ } else if (obj.command === "chime") {
664
+ this.log.debug(`snooze command - message: ${JSON.stringify(obj.message)}`);
665
+ if (typeof obj.message === "object" && typeof obj.message.station_sn === "string" && obj.message.station_sn !== "" && (obj.message.ringtone === void 0 || typeof obj.message.ringtone === "number")) {
666
+ try {
667
+ const station = await this.eufy.getStation(obj.message.station_sn);
668
+ if (station.hasCommand(import_eufy_security_client.CommandName.StationChime)) {
669
+ await station.chimeHomebase(obj.message.ringtone !== void 0 && typeof obj.message.ringtone === "number" ? obj.message.ringtone : 0);
670
+ if (obj.callback)
671
+ this.sendTo(obj.from, obj.command, {
672
+ sended: true,
673
+ result: "chime command sended"
674
+ }, obj.callback);
675
+ } else {
676
+ if (obj.callback)
677
+ this.sendTo(obj.from, obj.command, {
678
+ sended: false,
679
+ result: "chime command not supported by specified station"
680
+ }, obj.callback);
681
+ }
682
+ if (obj.callback)
683
+ this.sendTo(obj.from, obj.command, "chime command sended", obj.callback);
684
+ } catch (error) {
685
+ if (error instanceof import_eufy_security_client.StationNotFoundError) {
686
+ if (obj.callback)
687
+ this.sendTo(obj.from, obj.command, {
688
+ sended: false,
689
+ result: "snooze command not sended because specified station doesn't exists"
690
+ }, obj.callback);
691
+ } else {
692
+ throw error;
693
+ }
694
+ }
695
+ }
696
+ } else if (obj.command === "pollRefresh") {
697
+ this.log.debug(`pollRefresh command`);
698
+ await this.eufy.refreshCloudData();
699
+ if (obj.callback)
700
+ this.sendTo(obj.from, obj.command, {
701
+ sended: true,
702
+ result: "pollRefresh command sended"
703
+ }, obj.callback);
704
+ } else {
705
+ const errorMessage = `Received unknown message: ${JSON.stringify(obj.message)}`;
706
+ this.log.warn(errorMessage);
707
+ if (obj.callback)
708
+ this.sendTo(obj.from, obj.command, {
709
+ sended: false,
710
+ result: errorMessage
711
+ }, obj.callback);
712
+ }
713
+ } catch (error) {
714
+ const errorMessage = `Error during processing of received message: ${error instanceof Error ? `${error.name} - ${error.message}` : error}`;
715
+ this.log.error(errorMessage);
716
+ if (obj.callback)
717
+ this.sendTo(obj.from, obj.command, {
718
+ sended: false,
719
+ result: errorMessage
720
+ }, obj.callback);
721
+ }
722
+ }
723
+ }
424
724
  getStateCommon(property) {
425
725
  const state = {
426
726
  name: property.label,
@@ -428,7 +728,7 @@ class euSec extends utils.Adapter {
428
728
  role: "state",
429
729
  read: property.readable,
430
730
  write: property.writeable,
431
- def: property.default
731
+ def: property.type === "object" && typeof property.default === "object" ? JSON.stringify(property.default) : property.default
432
732
  };
433
733
  switch (property.type) {
434
734
  case "number": {
@@ -689,6 +989,19 @@ class euSec extends utils.Adapter {
689
989
  native: {}
690
990
  });
691
991
  }
992
+ if (device.hasCommand(import_eufy_security_client.CommandName.DeviceOpen)) {
993
+ await this.setObjectNotExistsAsync(device.getStateID(import_types.DeviceStateID.OPEN_BOX), {
994
+ type: "state",
995
+ common: {
996
+ name: "Open Box",
997
+ type: "boolean",
998
+ role: "button.start",
999
+ read: false,
1000
+ write: true
1001
+ },
1002
+ native: {}
1003
+ });
1004
+ }
692
1005
  if (device.hasCommand(import_eufy_security_client.CommandName.DeviceStartLivestream)) {
693
1006
  await this.setObjectNotExistsAsync(device.getStateID(import_types.DeviceStateID.START_STREAM), {
694
1007
  type: "state",
@@ -831,10 +1144,38 @@ class euSec extends utils.Adapter {
831
1144
  this.delObjectAsync(station.getStateID("", 0), { recursive: true }).catch((error) => {
832
1145
  this.logger.error(`Error deleting states of removed station`, error);
833
1146
  });
834
- import_fs_extra.default.remove(path.join(utils.getAbsoluteInstanceDataDir(this), station.getSerial())).catch((error) => {
1147
+ this.delFileAsync(this.namespace, station.getSerial()).catch((error) => {
835
1148
  this.logger.error(`Error deleting fs contents of removed station`, error);
836
1149
  });
837
1150
  }
1151
+ /*private async downloadEventVideo(device: Device, event_time: number, full_path: string | undefined, cipher_id: number | undefined): Promise<void> {
1152
+ this.logger.debug(`Device: ${device.getSerial()} full_path: ${full_path} cipher_id: ${cipher_id}`);
1153
+ try {
1154
+ if (!isEmpty(full_path) && cipher_id !== undefined) {
1155
+ const station = await this.eufy.getStation(device.getStationSerial());
1156
+
1157
+ if (station !== undefined) {
1158
+ if (this.downloadEvent[device.getSerial()])
1159
+ clearTimeout(this.downloadEvent[device.getSerial()]);
1160
+
1161
+ let videoLength = getVideoClipLength(device);
1162
+ const time_passed = (new Date().getTime() - new Date(event_time).getTime()) / 1000;
1163
+
1164
+ if (time_passed >= videoLength)
1165
+ videoLength = 1;
1166
+ else
1167
+ videoLength = videoLength - time_passed;
1168
+
1169
+ this.logger.info(`Downloading video event for device ${device.getSerial()} in ${videoLength} seconds...`);
1170
+ this.downloadEvent[device.getSerial()] = setTimeout(async () => {
1171
+ station.startDownload(device, full_path!, cipher_id);
1172
+ }, videoLength * 1000);
1173
+ }
1174
+ }
1175
+ } catch (error) {
1176
+ this.logger.error(`Device: ${device.getSerial()} - Error`, error);
1177
+ }
1178
+ }*/
838
1179
  async handlePushNotification(message) {
839
1180
  try {
840
1181
  if (message.device_sn !== void 0) {
@@ -895,8 +1236,10 @@ class euSec extends utils.Adapter {
895
1236
  const reg = new RegExp(`^${this.namespace}.[0-9A-Z]+.[a-z]+.[0-9A-Z]+$`);
896
1237
  for (const id of allDevices) {
897
1238
  if (id._id.match(reg)) {
898
- const serial = id._id.split(".")[4];
899
- if (!deviceSerials.includes(serial)) {
1239
+ const values = id._id.split(".");
1240
+ const stateChannel = values[3];
1241
+ const serial = values[4];
1242
+ if (!deviceSerials.includes(serial) || deviceSerials.includes(serial) && devices[deviceSerials.indexOf(serial)].getStateChannel() !== "unknown" && stateChannel === "unknown") {
900
1243
  await this.delObjectAsync(id._id, { recursive: true });
901
1244
  }
902
1245
  }
@@ -947,41 +1290,29 @@ class euSec extends utils.Adapter {
947
1290
  } catch (error) {
948
1291
  this.log.error(`Delete obsolete properties ERROR - ${JSON.stringify(error)}`);
949
1292
  }
950
- new Promise(async (resolve, reject) => {
951
- try {
952
- const dir_path = path.join(utils.getAbsoluteInstanceDataDir(this));
953
- if (import_fs_extra.default.existsSync(dir_path)) {
954
- for (const content of import_fs_extra.default.readdirSync(dir_path).filter((fn) => fn.match("^T[0-9A-Z]+$") !== null)) {
955
- if (!stationSerials.includes(content)) {
956
- import_fs_extra.default.removeSync(path.join(dir_path, content));
957
- } else {
958
- for (const dir of import_fs_extra.default.readdirSync(path.join(dir_path, content))) {
959
- if (dir === import_types.DataLocation.LIVESTREAM || dir === import_types.DataLocation.LAST_LIVESTREAM || dir === import_types.DataLocation.TEMP) {
960
- import_fs_extra.default.removeSync(path.join(dir_path, content, dir));
961
- } else {
962
- const files = import_fs_extra.default.readdirSync(path.join(dir_path, content, dir));
963
- let deletedFiles = 0;
964
- for (const file of files) {
965
- if (!deviceSerials.includes(file.substring(0, 16))) {
966
- import_fs_extra.default.removeSync(path.join(dir_path, content, dir, file));
967
- deletedFiles++;
968
- }
969
- }
970
- if (deletedFiles === files.length) {
971
- import_fs_extra.default.removeSync(path.join(dir_path, content, dir));
972
- }
973
- }
974
- }
1293
+ try {
1294
+ const contents = await this.readDirAsync(this.namespace, "");
1295
+ for (const content of contents.filter((fn) => fn.file.match("^T[0-9A-Z]+$") !== null && fn.isDir)) {
1296
+ if (!stationSerials.includes(content.file)) {
1297
+ await this.delFileAsync(this.namespace, content.file);
1298
+ } else {
1299
+ const dirContents = await this.readDirAsync(this.namespace, content.file);
1300
+ for (const dir of dirContents.filter((fn) => Object.values(import_types.DataLocation).includes(fn.file) && fn.isDir)) {
1301
+ const files = await this.readDirAsync(this.namespace, path.join(content.file, dir.file));
1302
+ let deletedFiles = 0;
1303
+ for (const file of files.filter((fn) => !deviceSerials.includes(fn.file.substring(0, 16)) && !fn.isDir)) {
1304
+ await this.delFileAsync(this.namespace, path.join(content.file, dir.file, file.file));
1305
+ deletedFiles++;
1306
+ }
1307
+ if (deletedFiles === files.length) {
1308
+ await this.delFileAsync(this.namespace, path.join(content.file, dir.file));
975
1309
  }
976
1310
  }
977
1311
  }
978
- resolve();
979
- } catch (error) {
980
- reject(error);
981
1312
  }
982
- }).catch((error) => {
983
- this.log.error(`Delete obsolete directories/files ERROR - ${JSON.stringify(error)}`);
984
- });
1313
+ } catch (error) {
1314
+ this.log.error(`Delete obsolete directories/files ERROR - ${error}`);
1315
+ }
985
1316
  }
986
1317
  async onClose() {
987
1318
  await this.setStateAsync("info.connection", { val: false, ack: true }).catch();
@@ -1074,16 +1405,7 @@ class euSec extends utils.Adapter {
1074
1405
  await this.setStateAsync("info.mqtt_connection", { val: false, ack: true });
1075
1406
  }
1076
1407
  async onStationCommandResult(station, result) {
1077
- if (result.return_code !== 0 && result.command_type === import_eufy_security_client.CommandType.CMD_START_REALTIME_MEDIA) {
1078
- this.logger.debug(`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}) fallback to RTMP livestream...`);
1079
- try {
1080
- const device = await this.eufy.getStationDevice(station.getSerial(), result.channel);
1081
- if (device.isCamera())
1082
- this.eufy.startCloudLivestream(device.getSerial());
1083
- } catch (error) {
1084
- this.logger.error(`Station: ${station.getSerial()} command ${import_eufy_security_client.CommandType[result.command_type]} RTMP fallback failed - Error ${error}`);
1085
- }
1086
- } else if (result.return_code !== 0 && result.command_type !== import_eufy_security_client.CommandType.P2P_QUERY_STATUS_IN_LOCK) {
1408
+ if (result.return_code !== 0 && result.command_type !== import_eufy_security_client.CommandType.P2P_QUERY_STATUS_IN_LOCK) {
1087
1409
  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})`);
1088
1410
  }
1089
1411
  }
@@ -1122,12 +1444,12 @@ class euSec extends utils.Adapter {
1122
1444
  try {
1123
1445
  const picture = value;
1124
1446
  const fileName = `${device.getSerial()}.${picture.type.ext}`;
1125
- const filePath = path.join(utils.getAbsoluteInstanceDataDir(this), device.getStationSerial(), import_types.DataLocation.LAST_EVENT);
1126
- if (!import_fs_extra.default.existsSync(filePath)) {
1127
- import_fs_extra.default.mkdirSync(filePath, { mode: 509, recursive: true });
1447
+ const filePath = path.join(device.getStationSerial(), import_types.DataLocation.LAST_EVENT);
1448
+ if (!await this.fileExistsAsync(this.namespace, filePath)) {
1449
+ await this.mkdirAsync(this.namespace, filePath);
1128
1450
  }
1129
- await import_fs_extra.default.writeFile(path.join(filePath, fileName), picture.data);
1130
- await (0, import_utils.setStateChangedAsync)(this, device.getStateID(import_types.DeviceStateID.PICTURE_URL), `/${this.namespace}/${device.getStationSerial()}/${import_types.DataLocation.LAST_EVENT}/${device.getSerial()}.${picture.type.ext}`);
1451
+ await this.writeFileAsync(this.namespace, path.join(filePath, fileName), picture.data);
1452
+ 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);
1131
1453
  await (0, import_utils.setStateChangedAsync)(this, device.getStateID(import_types.DeviceStateID.PICTURE_HTML), (0, import_utils.getImageAsHTML)(picture.data, picture.type.mime));
1132
1454
  } catch (err) {
1133
1455
  const error = (0, import_eufy_security_client.ensureError)(err);
@@ -1174,7 +1496,9 @@ class euSec extends utils.Adapter {
1174
1496
  try {
1175
1497
  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 });
1176
1498
  this.setStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM_RTSP), { val: `rtsp://${this.config.hostname}:${this.config.go2rtc_rtsp_port}/${device.getSerial()}`, ack: true });
1177
- await (0, import_video.ffmpegStreamToGo2rtc)(this.config, this.namespace, device.getSerial(), metadata, videostream, audiostream, this.logger);
1499
+ await (0, import_video.streamToGo2rtc)(device.getSerial(), videostream, audiostream, this.logger, this.config, this.namespace, metadata).catch((error) => {
1500
+ this.logger.debug(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Stopping livestream...`, error);
1501
+ });
1178
1502
  } catch (error) {
1179
1503
  this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Stopping livestream...`, error);
1180
1504
  this.eufy.stopStationLivestream(device.getSerial()).catch(async (error2) => {
@@ -1186,6 +1510,59 @@ class euSec extends utils.Adapter {
1186
1510
  this.delStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM));
1187
1511
  this.delStateAsync(device.getStateID(import_types.DeviceStateID.LIVESTREAM_RTSP));
1188
1512
  }
1513
+ /*private async onStationDownloadFinish(_station: Station, _device: Device): Promise<void> {
1514
+ //this.logger.trace(`Station: ${station.getSerial()} channel: ${channel}`);
1515
+ }*/
1516
+ /*private async onStationDownloadStart(station: Station, device: Device, metadata: StreamMetadata, videostream: Readable, audiostream: Readable): Promise<void> {
1517
+ try {
1518
+ //TODO: Deactivated because the decryption of the download has changed.
1519
+ await removeFiles(this, station.getSerial(), DataLocation.TEMP, device.getSerial()).catch();
1520
+ const file_path = getDataFilePath(this, station.getSerial(), DataLocation.TEMP, `${device.getSerial()}${STREAM_FILE_NAME_EXT}`);
1521
+
1522
+ await ffmpegStreamToHls(this.config, this.namespace, metadata, videostream, audiostream, file_path, this.logger)
1523
+ .then(async () => {
1524
+ if (fse.pathExistsSync(file_path)) {
1525
+ await removeFiles(this, station.getSerial(), DataLocation.LAST_EVENT, device.getSerial());
1526
+ return true;
1527
+ }
1528
+ return false;
1529
+ })
1530
+ .then(async (result) => {
1531
+ if (result)
1532
+ await moveFiles(this, station.getSerial(), device.getSerial(), DataLocation.TEMP, DataLocation.LAST_EVENT);
1533
+ return result;
1534
+ })
1535
+ .then(async (result) => {
1536
+ if (result) {
1537
+ const filename_without_ext = getDataFilePath(this, station.getSerial(), DataLocation.LAST_EVENT, device.getSerial());
1538
+ setStateAsync(this, device.getStateID(DeviceStateID.LAST_EVENT_VIDEO_URL), "Last captured video URL", `/${this.namespace}/${station.getSerial()}/${DataLocation.LAST_EVENT}/${device.getSerial()}${STREAM_FILE_NAME_EXT}`, "url");
1539
+ if (fse.pathExistsSync(`${filename_without_ext}${STREAM_FILE_NAME_EXT}`))
1540
+ await ffmpegPreviewImage(this.config, `${filename_without_ext}${STREAM_FILE_NAME_EXT}`, `${filename_without_ext}${IMAGE_FILE_JPEG_EXT}`, this.logger)
1541
+ .then(() => {
1542
+ setStateAsync(this, device.getStateID(DeviceStateID.LAST_EVENT_PIC_URL), "Last event picture URL", `/${this.namespace}/${station.getSerial()}/${DataLocation.LAST_EVENT}/${device.getSerial()}${IMAGE_FILE_JPEG_EXT}`, "url");
1543
+ try {
1544
+ if (fse.existsSync(`${filename_without_ext}${IMAGE_FILE_JPEG_EXT}`)) {
1545
+ const image_data = getImageAsHTML(fse.readFileSync(`${filename_without_ext}${IMAGE_FILE_JPEG_EXT}`));
1546
+ setStateAsync(this, device.getStateID(DeviceStateID.LAST_EVENT_PIC_HTML), "Last event picture HTML image", image_data, "html");
1547
+ }
1548
+ } catch (error) {
1549
+ this.logger.error(`Station: ${station.getSerial()} device: ${device.getSerial()} - Error`, error);
1550
+ }
1551
+ })
1552
+ .catch((error) => {
1553
+ this.logger.error(`ffmpegPreviewImage - station: ${station.getSerial()} device: ${device.getSerial()} - Error`, error);
1554
+ });
1555
+ }
1556
+ })
1557
+ .catch(async (error) => {
1558
+ this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Cancelling download...`, error);
1559
+ await this.eufy.cancelStationDownload(device.getSerial());
1560
+ });
1561
+ } catch(error) {
1562
+ this.logger.error(`Station: ${station.getSerial()} Device: ${device.getSerial()} - Error - Cancelling download...`, error);
1563
+ await this.eufy.cancelStationDownload(device.getSerial());
1564
+ }
1565
+ }*/
1189
1566
  onStationRTSPUrl(station, device, value) {
1190
1567
  (0, import_utils.setStateChangedAsync)(this, device.getStateID(import_types.DeviceStateID.RTSP_STREAM_URL), value);
1191
1568
  }
@@ -1233,4 +1610,8 @@ if (require.main !== module) {
1233
1610
  } else {
1234
1611
  (() => new euSec())();
1235
1612
  }
1613
+ // Annotate the CommonJS export names for ESM import in node:
1614
+ 0 && (module.exports = {
1615
+ euSec
1616
+ });
1236
1617
  //# sourceMappingURL=main.js.map