@iobroker/js-controller-adapter 7.2.3 → 8.0.0-alpha.1-20260921-e5941ca8a
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/cjs/index.d.ts +0 -1
- package/build/cjs/index.js +5 -8
- package/build/cjs/index.js.map +2 -2
- package/build/cjs/lib/_Types.d.ts +394 -63
- package/build/cjs/lib/adapter/adapter.d.ts +1818 -177
- package/build/cjs/lib/adapter/adapter.js +715 -1035
- package/build/cjs/lib/adapter/adapter.js.map +2 -2
- package/build/cjs/lib/adapter/asyncAdapter.d.ts +124 -0
- package/build/cjs/lib/adapter/asyncAdapter.js +184 -0
- package/build/cjs/lib/adapter/asyncAdapter.js.map +7 -0
- package/build/cjs/lib/adapter/context.d.ts +34 -0
- package/build/cjs/lib/adapter/context.js +17 -0
- package/build/cjs/lib/adapter/context.js.map +7 -0
- package/build/cjs/lib/adapter/defaultObjs.d.ts +5 -0
- package/build/cjs/lib/adapter/defaultObjs.js +2 -0
- package/build/cjs/lib/adapter/defaultObjs.js.map +2 -2
- package/build/cjs/lib/adapter/log.d.ts +25 -0
- package/build/cjs/lib/adapter/log.js +29 -0
- package/build/cjs/lib/adapter/log.js.map +2 -2
- package/build/cjs/lib/adapter/managers/AdapterContextBase.d.ts +49 -0
- package/build/cjs/lib/adapter/managers/AdapterContextBase.js +98 -0
- package/build/cjs/lib/adapter/managers/AdapterContextBase.js.map +7 -0
- package/build/cjs/lib/adapter/managers/AliasManager.d.ts +103 -0
- package/build/cjs/lib/adapter/managers/AliasManager.js +293 -0
- package/build/cjs/lib/adapter/managers/AliasManager.js.map +7 -0
- package/build/cjs/lib/adapter/managers/CertificateManager.d.ts +50 -0
- package/build/cjs/lib/adapter/managers/CertificateManager.js +174 -0
- package/build/cjs/lib/adapter/managers/CertificateManager.js.map +7 -0
- package/build/cjs/lib/adapter/managers/MessagingManager.d.ts +58 -0
- package/build/cjs/lib/adapter/managers/MessagingManager.js +289 -0
- package/build/cjs/lib/adapter/managers/MessagingManager.js.map +7 -0
- package/build/cjs/lib/adapter/managers/SubscriptionManager.d.ts +107 -0
- package/build/cjs/lib/adapter/managers/SubscriptionManager.js +349 -0
- package/build/cjs/lib/adapter/managers/SubscriptionManager.js.map +7 -0
- package/build/cjs/lib/adapter/userInterfaceMessagingController.d.ts +10 -0
- package/build/cjs/lib/adapter/userInterfaceMessagingController.js +9 -2
- package/build/cjs/lib/adapter/userInterfaceMessagingController.js.map +2 -2
- package/build/cjs/lib/adapter/utils.js +15 -1
- package/build/cjs/lib/adapter/utils.js.map +2 -2
- package/build/cjs/lib/adapter/validator.d.ts +10 -5
- package/build/cjs/lib/adapter/validator.js +6 -5
- package/build/cjs/lib/adapter/validator.js.map +2 -2
- package/build/esm/index.d.ts +0 -1
- package/build/esm/index.d.ts.map +1 -1
- package/build/esm/index.js +0 -1
- package/build/esm/index.js.map +1 -1
- package/build/esm/lib/_Types.d.ts +394 -63
- package/build/esm/lib/_Types.d.ts.map +1 -1
- package/build/esm/lib/adapter/adapter.d.ts +1818 -177
- package/build/esm/lib/adapter/adapter.d.ts.map +1 -1
- package/build/esm/lib/adapter/adapter.js +827 -1215
- package/build/esm/lib/adapter/adapter.js.map +1 -1
- package/build/esm/lib/adapter/asyncAdapter.d.ts +124 -0
- package/build/esm/lib/adapter/asyncAdapter.d.ts.map +1 -0
- package/build/esm/lib/adapter/asyncAdapter.js +157 -0
- package/build/esm/lib/adapter/asyncAdapter.js.map +1 -0
- package/build/esm/lib/adapter/context.d.ts +34 -0
- package/build/esm/lib/adapter/context.d.ts.map +1 -0
- package/build/esm/lib/adapter/context.js +2 -0
- package/build/esm/lib/adapter/context.js.map +1 -0
- package/build/esm/lib/adapter/defaultObjs.d.ts +5 -0
- package/build/esm/lib/adapter/defaultObjs.d.ts.map +1 -1
- package/build/esm/lib/adapter/defaultObjs.js +5 -0
- package/build/esm/lib/adapter/defaultObjs.js.map +1 -1
- package/build/esm/lib/adapter/log.d.ts +25 -0
- package/build/esm/lib/adapter/log.d.ts.map +1 -1
- package/build/esm/lib/adapter/log.js +25 -0
- package/build/esm/lib/adapter/log.js.map +1 -1
- package/build/esm/lib/adapter/managers/AdapterContextBase.d.ts +49 -0
- package/build/esm/lib/adapter/managers/AdapterContextBase.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/AdapterContextBase.js +77 -0
- package/build/esm/lib/adapter/managers/AdapterContextBase.js.map +1 -0
- package/build/esm/lib/adapter/managers/AliasManager.d.ts +103 -0
- package/build/esm/lib/adapter/managers/AliasManager.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/AliasManager.js +269 -0
- package/build/esm/lib/adapter/managers/AliasManager.js.map +1 -0
- package/build/esm/lib/adapter/managers/CertificateManager.d.ts +50 -0
- package/build/esm/lib/adapter/managers/CertificateManager.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/CertificateManager.js +142 -0
- package/build/esm/lib/adapter/managers/CertificateManager.js.map +1 -0
- package/build/esm/lib/adapter/managers/MessagingManager.d.ts +58 -0
- package/build/esm/lib/adapter/managers/MessagingManager.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/MessagingManager.js +267 -0
- package/build/esm/lib/adapter/managers/MessagingManager.js.map +1 -0
- package/build/esm/lib/adapter/managers/SubscriptionManager.d.ts +107 -0
- package/build/esm/lib/adapter/managers/SubscriptionManager.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/SubscriptionManager.js +374 -0
- package/build/esm/lib/adapter/managers/SubscriptionManager.js.map +1 -0
- package/build/esm/lib/adapter/userInterfaceMessagingController.d.ts +10 -0
- package/build/esm/lib/adapter/userInterfaceMessagingController.d.ts.map +1 -1
- package/build/esm/lib/adapter/userInterfaceMessagingController.js +8 -2
- package/build/esm/lib/adapter/userInterfaceMessagingController.js.map +1 -1
- package/build/esm/lib/adapter/utils.d.ts.map +1 -1
- package/build/esm/lib/adapter/utils.js +1 -1
- package/build/esm/lib/adapter/utils.js.map +1 -1
- package/build/esm/lib/adapter/validator.d.ts +10 -5
- package/build/esm/lib/adapter/validator.d.ts.map +1 -1
- package/build/esm/lib/adapter/validator.js +8 -5
- package/build/esm/lib/adapter/validator.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +25 -20
|
@@ -11,15 +11,19 @@ import extend from 'node.extend';
|
|
|
11
11
|
import yargs from 'yargs/yargs';
|
|
12
12
|
import fs from 'fs-extra';
|
|
13
13
|
import * as url from 'node:url';
|
|
14
|
-
import { PluginHandler } from '@iobroker/plugin-base';
|
|
15
14
|
import { EXIT_CODES, getObjectsConstructor, getStatesConstructor, isInstalledFromNpm, logger, password, tools, } from '@iobroker/js-controller-common';
|
|
16
|
-
import { decryptArray, encryptArray, getAdapterScopedPackageIdentifier, getSupportedFeatures, isMessageboxSupported, listInstalledNodeModules, requestModuleNameByUrl, deleteObjectAttribute,
|
|
15
|
+
import { decryptArray, encryptArray, getAdapterScopedPackageIdentifier, getSupportedFeatures, isMessageboxSupported, listInstalledNodeModules, requestModuleNameByUrl, deleteObjectAttribute, getObjectAttribute, } from '../../lib/adapter/utils.js';
|
|
17
16
|
// local version is always the same as controller version, since lerna exact: true is used
|
|
18
17
|
import packJson from '@iobroker/js-controller-adapter/package.json' with { type: 'json' };
|
|
19
18
|
import { Log } from '../../lib/adapter/log.js';
|
|
20
19
|
import { Validator } from './validator.js';
|
|
21
20
|
import { ACCESS_EVERY_READ, ACCESS_EVERY_WRITE, ACCESS_GROUP_READ, ACCESS_GROUP_WRITE, ACCESS_USER_READ, ACCESS_USER_WRITE, ALIAS_STARTS_WITH, DEFAULT_SECRET, ERROR_PERMISSION, NO_PROTECT_ADAPTERS, STATE_QUALITY, SYSTEM_ADMIN_GROUP, SYSTEM_ADMIN_USER, } from '../../lib/adapter/constants.js';
|
|
21
|
+
import { PluginHandler } from '@iobroker/plugin-base';
|
|
22
22
|
import { UserInterfaceMessagingController } from '../../lib/adapter/userInterfaceMessagingController.js';
|
|
23
|
+
import { AsyncAdapter } from '../../lib/adapter/asyncAdapter.js';
|
|
24
|
+
import { CERTIFICATES_OBJECT_ID } from '../../lib/adapter/managers/CertificateManager.js';
|
|
25
|
+
import { AliasManager } from '../../lib/adapter/managers/AliasManager.js';
|
|
26
|
+
import { SubscriptionManager } from '../../lib/adapter/managers/SubscriptionManager.js';
|
|
23
27
|
import { SYSTEM_ADAPTER_PREFIX, DEFAULT_OBJECTS_WARN_LIMIT } from '@iobroker/js-controller-common-db/constants';
|
|
24
28
|
import { isLogLevel } from '@iobroker/js-controller-common-db/tools';
|
|
25
29
|
const controllerVersion = packJson.version;
|
|
@@ -51,14 +55,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
51
55
|
startedInCompactMode;
|
|
52
56
|
/** List of instances which want our logs */
|
|
53
57
|
logList = new Set();
|
|
54
|
-
aliases = new Map();
|
|
55
|
-
aliasPatterns = new Set();
|
|
56
58
|
enums = {};
|
|
57
59
|
eventLoopLags = [];
|
|
58
60
|
overwriteLogLevel = false;
|
|
59
61
|
adapterReady = false;
|
|
60
|
-
/** Callbacks from sendTo */
|
|
61
|
-
messageCallbacks = new Map();
|
|
62
62
|
/**
|
|
63
63
|
* Contains a live cache of the adapter's states.
|
|
64
64
|
* NOTE: This is only defined if the adapter was initialized with the option states: true.
|
|
@@ -70,7 +70,6 @@ export class AdapterClass extends EventEmitter {
|
|
|
70
70
|
*/
|
|
71
71
|
oObjects;
|
|
72
72
|
_stopInProgress = false;
|
|
73
|
-
_callbackId = 1;
|
|
74
73
|
_firstConnection = true;
|
|
75
74
|
_timers = new Set();
|
|
76
75
|
_intervals = new Set();
|
|
@@ -79,12 +78,14 @@ export class AdapterClass extends EventEmitter {
|
|
|
79
78
|
log;
|
|
80
79
|
performStrictObjectChecks;
|
|
81
80
|
_logger;
|
|
82
|
-
_restartScheduleJob;
|
|
81
|
+
_restartScheduleJob = null;
|
|
83
82
|
_schedule;
|
|
84
83
|
namespaceLog;
|
|
85
84
|
namespace;
|
|
86
85
|
name;
|
|
87
86
|
_systemSecret;
|
|
87
|
+
/** Attributes of `encryptedNative`: their values in `this.config` are decrypted on start */
|
|
88
|
+
_decryptedNativeAttributes = new Array();
|
|
88
89
|
/** Whether the adapter has already terminated */
|
|
89
90
|
terminated = false;
|
|
90
91
|
/** The cache of usernames */
|
|
@@ -97,8 +98,6 @@ export class AdapterClass extends EventEmitter {
|
|
|
97
98
|
users = {}; // todo
|
|
98
99
|
/** The cache of user groups */
|
|
99
100
|
groups = {};
|
|
100
|
-
/** An array of instances, that support auto subscribe */
|
|
101
|
-
autoSubscribe = [];
|
|
102
101
|
defaultHistory = null;
|
|
103
102
|
pluginHandler;
|
|
104
103
|
_reportInterval;
|
|
@@ -130,11 +129,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
130
129
|
/** latitude configured in system.config, only available if requested via AdapterOptions `useFormatDate`*/
|
|
131
130
|
latitude;
|
|
132
131
|
_defaultObjs;
|
|
133
|
-
_aliasObjectsSubscribed;
|
|
134
132
|
config = {};
|
|
135
133
|
host;
|
|
136
134
|
common;
|
|
137
|
-
mboxSubscribed;
|
|
138
135
|
/** Stop the adapter */
|
|
139
136
|
stop;
|
|
140
137
|
version;
|
|
@@ -162,6 +159,31 @@ export class AdapterClass extends EventEmitter {
|
|
|
162
159
|
SUPPORTED_FEATURES = getSupportedFeatures();
|
|
163
160
|
/** Controller for messaging related functionality */
|
|
164
161
|
uiMessagingController;
|
|
162
|
+
/** Shared runtime context handed to managers */
|
|
163
|
+
#context;
|
|
164
|
+
#async;
|
|
165
|
+
#certWatchers = new Map();
|
|
166
|
+
#aliasInstance;
|
|
167
|
+
/** Lazily-constructed alias subscription manager. */
|
|
168
|
+
get #alias() {
|
|
169
|
+
return (this.#aliasInstance ??= new AliasManager(this.#context, (id, allowSystem, options) => this._utils.validateId(id, allowSystem, options)));
|
|
170
|
+
}
|
|
171
|
+
#subscriptionsInstance;
|
|
172
|
+
/** Lazily-constructed object/file/state subscription manager. */
|
|
173
|
+
get #subscriptions() {
|
|
174
|
+
return (this.#subscriptionsInstance ??= new SubscriptionManager(this.#context, (id, isPattern) => this._utils.fixId(id, isPattern), this.#alias, {
|
|
175
|
+
getForeignStateObjects: (pattern, options) =>
|
|
176
|
+
// @ts-expect-error adjust types
|
|
177
|
+
this.getForeignObjectsAsync(pattern, null, null, options),
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
/** True once `system.certificates` is subscribed, i.e. certificates have been requested */
|
|
181
|
+
#certObjectSubscribed = false;
|
|
182
|
+
/** True once a certificate change scheduled a restart, so concurrent changes schedule only one */
|
|
183
|
+
#certRestartScheduled = false;
|
|
184
|
+
/**
|
|
185
|
+
* @param options Adapter options, or the adapter name as a string
|
|
186
|
+
*/
|
|
165
187
|
constructor(options) {
|
|
166
188
|
super();
|
|
167
189
|
// enable "const adapter = require(__dirname + '/../../lib/adapter.js')('adapterName');" call
|
|
@@ -299,6 +321,36 @@ export class AdapterClass extends EventEmitter {
|
|
|
299
321
|
subscribeCallback: this._options.uiClientSubscribe,
|
|
300
322
|
unsubscribeCallback: this._options.uiClientUnsubscribe,
|
|
301
323
|
});
|
|
324
|
+
const self = this;
|
|
325
|
+
this.#context = {
|
|
326
|
+
logger: this._logger,
|
|
327
|
+
uiMessagingController: this.uiMessagingController,
|
|
328
|
+
countOutput: () => {
|
|
329
|
+
self.outputCount++;
|
|
330
|
+
},
|
|
331
|
+
get namespace() {
|
|
332
|
+
return self.namespace;
|
|
333
|
+
},
|
|
334
|
+
get namespaceLog() {
|
|
335
|
+
return self.namespaceLog;
|
|
336
|
+
},
|
|
337
|
+
get host() {
|
|
338
|
+
return self.host;
|
|
339
|
+
},
|
|
340
|
+
get states() {
|
|
341
|
+
return self.#states;
|
|
342
|
+
},
|
|
343
|
+
get objects() {
|
|
344
|
+
return self.#objects;
|
|
345
|
+
},
|
|
346
|
+
get common() {
|
|
347
|
+
return self.common;
|
|
348
|
+
},
|
|
349
|
+
get config() {
|
|
350
|
+
return self.config;
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
this.#async = new AsyncAdapter(this.#context);
|
|
302
354
|
// Create dynamic methods
|
|
303
355
|
/**
|
|
304
356
|
* Promise-version of `Adapter.getPort`
|
|
@@ -320,10 +372,6 @@ export class AdapterClass extends EventEmitter {
|
|
|
320
372
|
* Promise-version of `Adapter.calculatePermissions`
|
|
321
373
|
*/
|
|
322
374
|
this.calculatePermissionsAsync = tools.promisifyNoError(this.calculatePermissions, this);
|
|
323
|
-
/**
|
|
324
|
-
* Promise-version of `Adapter.getCertificates`
|
|
325
|
-
*/
|
|
326
|
-
this.getCertificatesAsync = tools.promisify(this.getCertificates, this);
|
|
327
375
|
/**
|
|
328
376
|
* Promise-version of `Adapter.setObject`
|
|
329
377
|
*/
|
|
@@ -508,10 +556,6 @@ export class AdapterClass extends EventEmitter {
|
|
|
508
556
|
* Promise-version of `Adapter.fileExists`
|
|
509
557
|
*/
|
|
510
558
|
this.fileExistsAsync = tools.promisify(this.fileExists, this);
|
|
511
|
-
/**
|
|
512
|
-
* Promise-version of `Adapter.sendToHost`
|
|
513
|
-
*/
|
|
514
|
-
this.sendToHostAsync = tools.promisifyNoError(this.sendToHost, this);
|
|
515
559
|
/**
|
|
516
560
|
* Promise-version of `Adapter.setState`
|
|
517
561
|
*/
|
|
@@ -573,7 +617,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
573
617
|
*/
|
|
574
618
|
this.unsubscribeStatesAsync = tools.promisify(this.unsubscribeStates, this);
|
|
575
619
|
this.setExecutableCapabilities = tools.setExecutableCapabilities;
|
|
576
|
-
this._init();
|
|
620
|
+
this._init().catch(e => this._logger.error(`${this.namespaceLog} Cannot initialize adapter: ${e.message}`));
|
|
577
621
|
}
|
|
578
622
|
/**
|
|
579
623
|
* Get the adapter scoped package identifier of a node module
|
|
@@ -664,6 +708,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
664
708
|
return tools.encrypt(secretVal, value);
|
|
665
709
|
}
|
|
666
710
|
// unknown guard implementation
|
|
711
|
+
/**
|
|
712
|
+
* Read a session from the states DB
|
|
713
|
+
*
|
|
714
|
+
* @param id the session id
|
|
715
|
+
* @param callback return result
|
|
716
|
+
*/
|
|
667
717
|
getSession(id, callback) {
|
|
668
718
|
Validator.assertString(id, 'id');
|
|
669
719
|
Validator.assertCallback(callback, 'callback');
|
|
@@ -674,11 +724,19 @@ export class AdapterClass extends EventEmitter {
|
|
|
674
724
|
if (!this.#states) {
|
|
675
725
|
// if states is no longer existing, we do not need to unsubscribe
|
|
676
726
|
this._logger.info(`${this.namespaceLog} getSession not processed because States database not connected`);
|
|
677
|
-
return tools.
|
|
727
|
+
return tools.maybeCallback(options.callback, null);
|
|
678
728
|
}
|
|
679
|
-
this.#states.getSession(options.id, options.callback);
|
|
729
|
+
this.#states.getSession(options.id, (err, session) => tools.maybeCallback(options.callback, session || null));
|
|
680
730
|
}
|
|
681
731
|
// unknown implementation guards
|
|
732
|
+
/**
|
|
733
|
+
* Store a session in the states DB
|
|
734
|
+
*
|
|
735
|
+
* @param id the session id
|
|
736
|
+
* @param ttl time to live in seconds
|
|
737
|
+
* @param data the session data to store
|
|
738
|
+
* @param callback return result
|
|
739
|
+
*/
|
|
682
740
|
setSession(id, ttl, data, callback) {
|
|
683
741
|
Validator.assertString(id, 'id');
|
|
684
742
|
Validator.assertOptionalCallback(callback, 'callback');
|
|
@@ -693,8 +751,16 @@ export class AdapterClass extends EventEmitter {
|
|
|
693
751
|
this._logger.info(`${this.namespaceLog} setSession not processed because States database not connected`);
|
|
694
752
|
return tools.maybeCallbackWithError(options.callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
695
753
|
}
|
|
696
|
-
|
|
754
|
+
return options.callback
|
|
755
|
+
? this.#states.setSession(options.id, options.ttl, options.data, options.callback)
|
|
756
|
+
: this.#states.setSession(options.id, options.ttl, options.data);
|
|
697
757
|
}
|
|
758
|
+
/**
|
|
759
|
+
* Destroy a session in the states DB
|
|
760
|
+
*
|
|
761
|
+
* @param id the session id
|
|
762
|
+
* @param callback return result
|
|
763
|
+
*/
|
|
698
764
|
destroySession(id, callback) {
|
|
699
765
|
Validator.assertString(id, 'id');
|
|
700
766
|
Validator.assertOptionalCallback(callback, 'callback');
|
|
@@ -706,12 +772,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
706
772
|
this._logger.info(`${this.namespaceLog} destroySession not processed because States database not connected`);
|
|
707
773
|
return tools.maybeCallbackWithError(options.callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
708
774
|
}
|
|
709
|
-
|
|
775
|
+
return options.callback
|
|
776
|
+
? this.#states.destroySession(options.id, options.callback)
|
|
777
|
+
: this.#states.destroySession(options.id);
|
|
710
778
|
}
|
|
711
779
|
async _getObjectsByArray(keys, options) {
|
|
712
780
|
try {
|
|
713
|
-
|
|
714
|
-
return res;
|
|
781
|
+
return await this.#objects.getObjects(keys, options);
|
|
715
782
|
}
|
|
716
783
|
catch (e) {
|
|
717
784
|
this._logger.error(`Could not get objects by array: ${e.message}`);
|
|
@@ -1066,7 +1133,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
1066
1133
|
options.user = this.usernames[options.user].id;
|
|
1067
1134
|
}
|
|
1068
1135
|
}
|
|
1069
|
-
this.getForeignObject(options.user, options, (err, obj) => {
|
|
1136
|
+
this.getForeignObject(options.user, options.options, (err, obj) => {
|
|
1070
1137
|
if (err || !obj) {
|
|
1071
1138
|
return tools.maybeCallbackWithError(options.callback, 'User does not exist');
|
|
1072
1139
|
}
|
|
@@ -1156,11 +1223,11 @@ export class AdapterClass extends EventEmitter {
|
|
|
1156
1223
|
if (options.group && !options.group.startsWith('system.group.')) {
|
|
1157
1224
|
options.group = `system.group.${options.group}`;
|
|
1158
1225
|
}
|
|
1159
|
-
this.getForeignObject(options.user, options, (err, obj) => {
|
|
1226
|
+
this.getForeignObject(options.user, options.options, (err, obj) => {
|
|
1160
1227
|
if (err || !obj) {
|
|
1161
1228
|
return tools.maybeCallback(options.callback, false);
|
|
1162
1229
|
}
|
|
1163
|
-
this.getForeignObject(options.group, options, (err, obj) => {
|
|
1230
|
+
return this.getForeignObject(options.group, options.options, (err, obj) => {
|
|
1164
1231
|
if (err || !obj) {
|
|
1165
1232
|
return tools.maybeCallback(options.callback, false);
|
|
1166
1233
|
}
|
|
@@ -1265,7 +1332,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
1265
1332
|
* ```
|
|
1266
1333
|
*/
|
|
1267
1334
|
calculatePermissions(user, commandsPermissions, options, callback) {
|
|
1268
|
-
user
|
|
1335
|
+
user ||= '';
|
|
1269
1336
|
if (typeof options === 'function') {
|
|
1270
1337
|
callback = options;
|
|
1271
1338
|
options = null;
|
|
@@ -1278,7 +1345,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
1278
1345
|
Validator.assertObject(options, 'options');
|
|
1279
1346
|
}
|
|
1280
1347
|
Validator.assertOptionalCallback(callback, 'callback');
|
|
1281
|
-
return this._calculatePermissions({
|
|
1348
|
+
return this._calculatePermissions({
|
|
1349
|
+
user: user,
|
|
1350
|
+
commandsPermissions,
|
|
1351
|
+
options,
|
|
1352
|
+
callback,
|
|
1353
|
+
});
|
|
1282
1354
|
}
|
|
1283
1355
|
async _calculatePermissions(options) {
|
|
1284
1356
|
const { user } = options;
|
|
@@ -1380,23 +1452,22 @@ export class AdapterClass extends EventEmitter {
|
|
|
1380
1452
|
for (const type of Object.keys(gAcl)) {
|
|
1381
1453
|
// fix bug. Some version have user instead of users.
|
|
1382
1454
|
if (type === 'user') {
|
|
1383
|
-
|
|
1384
|
-
acl.users = acl.users || {};
|
|
1455
|
+
acl.users ||= {};
|
|
1385
1456
|
}
|
|
1386
1457
|
else {
|
|
1387
|
-
// @ts-expect-error fix
|
|
1388
|
-
acl[type]
|
|
1458
|
+
// @ts-expect-error fix later
|
|
1459
|
+
acl[type] ||= {};
|
|
1389
1460
|
}
|
|
1390
1461
|
// @ts-expect-error fix it
|
|
1391
1462
|
for (const op of Object.keys(gAcl[type])) {
|
|
1392
1463
|
// fix error
|
|
1393
1464
|
if (type === 'user') {
|
|
1394
|
-
// @ts-expect-error fix
|
|
1395
|
-
acl.users[op]
|
|
1465
|
+
// @ts-expect-error fix later
|
|
1466
|
+
acl.users[op] ||= gAcl.user[op];
|
|
1396
1467
|
}
|
|
1397
1468
|
else {
|
|
1398
|
-
// @ts-expect-error fix
|
|
1399
|
-
acl[type][op]
|
|
1469
|
+
// @ts-expect-error fix later
|
|
1470
|
+
acl[type][op] ||= gAcl[type][op];
|
|
1400
1471
|
}
|
|
1401
1472
|
}
|
|
1402
1473
|
}
|
|
@@ -1443,10 +1514,8 @@ export class AdapterClass extends EventEmitter {
|
|
|
1443
1514
|
this._delays.forEach(timer => clearTimeout(timer));
|
|
1444
1515
|
this._delays.clear();
|
|
1445
1516
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
this.messageCallbacks.clear();
|
|
1449
|
-
}
|
|
1517
|
+
this.#async.clearPending();
|
|
1518
|
+
await this.stopWatchingCertificates();
|
|
1450
1519
|
if (this.#states && updateAliveState) {
|
|
1451
1520
|
this.outputCount++;
|
|
1452
1521
|
try {
|
|
@@ -1467,11 +1536,14 @@ export class AdapterClass extends EventEmitter {
|
|
|
1467
1536
|
if (this._options.unload.length >= 1) {
|
|
1468
1537
|
// The method takes (at least) a callback
|
|
1469
1538
|
try {
|
|
1470
|
-
this._options.unload(finishUnload);
|
|
1539
|
+
const unloadResult = this._options.unload(finishUnload);
|
|
1540
|
+
if (unloadResult instanceof Promise) {
|
|
1541
|
+
unloadResult.catch(e => this._logger.error(`${this.namespaceLog} Error in unload: ${e.message}`));
|
|
1542
|
+
}
|
|
1471
1543
|
}
|
|
1472
1544
|
catch {
|
|
1473
1545
|
// Can only throw in unload logic, in this case the finishUnload() was not called
|
|
1474
|
-
finishUnload();
|
|
1546
|
+
finishUnload().catch(e => this._logger.error(`${this.namespaceLog} Error in finishUnload: ${e.message}`));
|
|
1475
1547
|
}
|
|
1476
1548
|
}
|
|
1477
1549
|
else {
|
|
@@ -1484,7 +1556,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
1484
1556
|
await unloadPromise;
|
|
1485
1557
|
}
|
|
1486
1558
|
finally {
|
|
1487
|
-
finishUnload();
|
|
1559
|
+
finishUnload().catch(e => this._logger.error(`${this.namespaceLog} Error in finishUnload: ${e.message}`));
|
|
1488
1560
|
}
|
|
1489
1561
|
}
|
|
1490
1562
|
else {
|
|
@@ -1514,30 +1586,105 @@ export class AdapterClass extends EventEmitter {
|
|
|
1514
1586
|
}
|
|
1515
1587
|
}
|
|
1516
1588
|
/**
|
|
1517
|
-
*
|
|
1518
|
-
*
|
|
1589
|
+
* Installs a restart-on-change watcher for each file-backed certificate path, deduped by path.
|
|
1590
|
+
* A change schedules a graceful restart so the adapter reloads the new certificate.
|
|
1519
1591
|
*
|
|
1520
|
-
* @param
|
|
1592
|
+
* @param paths file-backed certificate paths returned by the certificate manager
|
|
1521
1593
|
*/
|
|
1522
|
-
|
|
1523
|
-
|
|
1594
|
+
#watchCertFiles(paths) {
|
|
1595
|
+
for (const certFile of paths) {
|
|
1596
|
+
if (this.#certWatchers.has(certFile)) {
|
|
1597
|
+
continue;
|
|
1598
|
+
}
|
|
1524
1599
|
try {
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
// start watcher of this file
|
|
1530
|
-
fs.watch(certFile, (eventType, filename) => {
|
|
1531
|
-
this._logger.warn(`${this.namespaceLog} New certificate "${filename}" detected. Restart adapter`);
|
|
1532
|
-
setTimeout(() => this._stop({ isPause: false, isScheduled: true }), 2_000);
|
|
1533
|
-
});
|
|
1534
|
-
}
|
|
1600
|
+
const watcher = fs.watch(certFile, (_eventType, filename) => {
|
|
1601
|
+
this.#scheduleCertificateRestart(`New certificate "${filename}" detected`);
|
|
1602
|
+
});
|
|
1603
|
+
this.#certWatchers.set(certFile, watcher);
|
|
1535
1604
|
}
|
|
1536
1605
|
catch (e) {
|
|
1537
|
-
this._logger.error(`${this.namespaceLog}
|
|
1606
|
+
this._logger.error(`${this.namespaceLog} Cannot watch certificate file ${certFile}: ${e.message}`);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Schedules a graceful restart, so the adapter reloads its certificates. Called by both the
|
|
1612
|
+
* file watchers and the `system.certificates` subscription; the first caller wins, so a change
|
|
1613
|
+
* seen through both channels restarts the adapter only once.
|
|
1614
|
+
*
|
|
1615
|
+
* @param reason human-readable cause, logged as a warning
|
|
1616
|
+
*/
|
|
1617
|
+
#scheduleCertificateRestart(reason) {
|
|
1618
|
+
if (this.#certRestartScheduled) {
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
this.#certRestartScheduled = true;
|
|
1622
|
+
this._logger.warn(`${this.namespaceLog} ${reason}. Restart adapter`);
|
|
1623
|
+
setTimeout(() => this._stop({ isPause: false, isScheduled: true }), 2_000);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Subscribes to the `system.certificates` object, so inline (text) certificates are watched the
|
|
1627
|
+
* same way file-backed ones are watched on disk. Subscribing on the system level channel keeps
|
|
1628
|
+
* these events out of the adapter's own `objectChange` handler.
|
|
1629
|
+
*
|
|
1630
|
+
* Called after certificates have been requested, so only adapters actually using certificates
|
|
1631
|
+
* pay for the subscription.
|
|
1632
|
+
*/
|
|
1633
|
+
async #subscribeCertObject() {
|
|
1634
|
+
if (this.#certObjectSubscribed || !this.#objects) {
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
// set before awaiting, so concurrent getCertificates calls subscribe only once
|
|
1638
|
+
this.#certObjectSubscribed = true;
|
|
1639
|
+
try {
|
|
1640
|
+
await this.#objects.subscribeAsync(CERTIFICATES_OBJECT_ID);
|
|
1641
|
+
// stopWatchingCertificates() may have run while we were awaiting; it cleared the flag but could
|
|
1642
|
+
// not unsubscribe a subscription that did not exist yet, so undo it now to avoid a leak.
|
|
1643
|
+
if (!this.#certObjectSubscribed && this.#objects) {
|
|
1644
|
+
await this.#objects.unsubscribeAsync(CERTIFICATES_OBJECT_ID);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
catch (e) {
|
|
1648
|
+
this.#certObjectSubscribed = false;
|
|
1649
|
+
this._logger.error(`${this.namespaceLog} Cannot watch certificate object ${CERTIFICATES_OBJECT_ID}: ${e.message}`);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Tears down the certificate watching installed by {@link getCertificatesAsync}: closes the file
|
|
1654
|
+
* watchers, unsubscribes from `system.certificates`, and forgets the certificates handed out by the
|
|
1655
|
+
* last call, so a later change no longer schedules a restart. Called on adapter stop.
|
|
1656
|
+
*/
|
|
1657
|
+
async stopWatchingCertificates() {
|
|
1658
|
+
for (const watcher of this.#certWatchers.values()) {
|
|
1659
|
+
watcher.close();
|
|
1660
|
+
}
|
|
1661
|
+
this.#certWatchers.clear();
|
|
1662
|
+
this.#certRestartScheduled = false;
|
|
1663
|
+
this.#async.stopWatchingCertificates();
|
|
1664
|
+
if (this.#certObjectSubscribed && this.#objects) {
|
|
1665
|
+
this.#certObjectSubscribed = false;
|
|
1666
|
+
try {
|
|
1667
|
+
await this.#objects.unsubscribeAsync(CERTIFICATES_OBJECT_ID);
|
|
1668
|
+
}
|
|
1669
|
+
catch {
|
|
1670
|
+
// ignore, e.g. when the objects DB is already gone
|
|
1538
1671
|
}
|
|
1539
1672
|
}
|
|
1540
|
-
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Loads SSL certificates by name and installs restart-on-change watchers: file watchers for
|
|
1676
|
+
* file-backed certificate values, and a subscription to `system.certificates` covering inline
|
|
1677
|
+
* ones and repointed paths.
|
|
1678
|
+
*
|
|
1679
|
+
* @param publicName public certificate name (defaults to `config.certPublic`)
|
|
1680
|
+
* @param privateName private key name (defaults to `config.certPrivate`)
|
|
1681
|
+
* @param chainedName chained certificate name (defaults to `config.certChained`)
|
|
1682
|
+
*/
|
|
1683
|
+
async getCertificatesAsync(publicName, privateName, chainedName) {
|
|
1684
|
+
const { certs, useLetsEncrypt, certFilePaths } = await this.#async.getCertificates(publicName, privateName, chainedName);
|
|
1685
|
+
this.#watchCertFiles(certFilePaths);
|
|
1686
|
+
await this.#subscribeCertObject();
|
|
1687
|
+
return [certs, useLetsEncrypt];
|
|
1541
1688
|
}
|
|
1542
1689
|
/**
|
|
1543
1690
|
* returns SSL certificates by name
|
|
@@ -1571,68 +1718,16 @@ export class AdapterClass extends EventEmitter {
|
|
|
1571
1718
|
callback = chainedName;
|
|
1572
1719
|
chainedName = undefined;
|
|
1573
1720
|
}
|
|
1574
|
-
const config = this.config;
|
|
1575
|
-
publicName = publicName || config.certPublic;
|
|
1576
|
-
privateName = privateName || config.certPrivate;
|
|
1577
|
-
chainedName = chainedName || config.certChained;
|
|
1578
|
-
if (publicName !== undefined) {
|
|
1579
|
-
Validator.assertString(publicName, 'publicName');
|
|
1580
|
-
}
|
|
1581
|
-
if (privateName !== undefined) {
|
|
1582
|
-
Validator.assertString(privateName, 'privateName');
|
|
1583
|
-
}
|
|
1584
|
-
if (chainedName !== undefined) {
|
|
1585
|
-
Validator.assertString(chainedName, 'chainedName');
|
|
1586
|
-
}
|
|
1587
1721
|
Validator.assertOptionalCallback(callback, 'callback');
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
1596
|
-
}
|
|
1597
|
-
try {
|
|
1598
|
-
// Load certificates
|
|
1599
|
-
obj = await this.#objects.getObject('system.certificates');
|
|
1600
|
-
}
|
|
1601
|
-
catch {
|
|
1602
|
-
// ignore
|
|
1603
|
-
}
|
|
1604
|
-
if (!obj ||
|
|
1605
|
-
!obj.native.certificates ||
|
|
1606
|
-
!publicName ||
|
|
1607
|
-
!privateName ||
|
|
1608
|
-
!obj.native.certificates[publicName] ||
|
|
1609
|
-
!obj.native.certificates[privateName] ||
|
|
1610
|
-
(chainedName && !obj.native.certificates[chainedName])) {
|
|
1611
|
-
this._logger.error(`${this.namespaceLog} Cannot configure secure web server, because no certificates found: ${publicName}, ${privateName}, ${chainedName}`);
|
|
1612
|
-
if (publicName === 'defaultPublic' || privateName === 'defaultPrivate') {
|
|
1613
|
-
this._logger.info(`${this.namespaceLog} Default certificates seem to be configured but missing. You can execute "iobroker cert create" in your shell to create these.`);
|
|
1614
|
-
}
|
|
1615
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_NOT_FOUND);
|
|
1616
|
-
}
|
|
1617
|
-
let ca;
|
|
1618
|
-
if (chainedName) {
|
|
1619
|
-
const chained = this._readFileCertificate(obj.native.certificates[chainedName]).split('-----END CERTIFICATE-----\r\n');
|
|
1620
|
-
// it is still a file name, and the file maybe does not exist, but we can omit this error
|
|
1621
|
-
if (chained.join('').length >= 512) {
|
|
1622
|
-
const caArr = [];
|
|
1623
|
-
for (const cert of chained) {
|
|
1624
|
-
if (cert.replace(/(\r\n|\r|\n)/g, '').trim()) {
|
|
1625
|
-
caArr.push(`${cert}-----END CERTIFICATE-----\r\n`);
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
ca = caArr.join('');
|
|
1629
|
-
}
|
|
1722
|
+
const cb = callback;
|
|
1723
|
+
if (typeof cb === 'function') {
|
|
1724
|
+
return this.getCertificatesAsync(publicName, privateName, chainedName).then(([certs, useLetsEncrypt]) => {
|
|
1725
|
+
cb(null, certs, useLetsEncrypt);
|
|
1726
|
+
}, (err) => {
|
|
1727
|
+
cb(err);
|
|
1728
|
+
});
|
|
1630
1729
|
}
|
|
1631
|
-
return
|
|
1632
|
-
key: this._readFileCertificate(obj.native.certificates[privateName]),
|
|
1633
|
-
cert: this._readFileCertificate(obj.native.certificates[publicName]),
|
|
1634
|
-
ca,
|
|
1635
|
-
}, obj.native.letsEncrypt);
|
|
1730
|
+
return this.getCertificatesAsync(publicName, privateName, chainedName);
|
|
1636
1731
|
}
|
|
1637
1732
|
/**
|
|
1638
1733
|
* Restarts an instance of the adapter.
|
|
@@ -1641,7 +1736,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
1641
1736
|
restart() {
|
|
1642
1737
|
if (this.stop) {
|
|
1643
1738
|
this._logger.warn(`${this.namespaceLog} Restart initiated`);
|
|
1644
|
-
this.stop();
|
|
1739
|
+
this.stop().catch(e => this._logger.error(`${this.namespaceLog} Cannot stop adapter: ${e.message}`));
|
|
1645
1740
|
}
|
|
1646
1741
|
else {
|
|
1647
1742
|
this._logger.warn(`${this.namespaceLog} Cannot initiate restart, because this.stop is not defined`);
|
|
@@ -1667,7 +1762,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
1667
1762
|
const configObjId = `system.adapter.${this.namespace}`;
|
|
1668
1763
|
let obj;
|
|
1669
1764
|
try {
|
|
1670
|
-
obj = await this.getForeignObjectAsync(configObjId);
|
|
1765
|
+
obj = (await this.getForeignObjectAsync(configObjId));
|
|
1671
1766
|
}
|
|
1672
1767
|
catch (e) {
|
|
1673
1768
|
this._logger.error(`${this.namespaceLog} Updating the adapter config failed: ${e.message}`);
|
|
@@ -1711,7 +1806,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
1711
1806
|
return this.setForeignObjectAsync(configObjId, obj);
|
|
1712
1807
|
}
|
|
1713
1808
|
/**
|
|
1714
|
-
* Reads
|
|
1809
|
+
* Reads a parameter which is stored encrypted and returns its decrypted value.
|
|
1715
1810
|
*
|
|
1716
1811
|
* It returns promise if no callback is provided.
|
|
1717
1812
|
*
|
|
@@ -1726,14 +1821,16 @@ export class AdapterClass extends EventEmitter {
|
|
|
1726
1821
|
async _getEncryptedConfig(options) {
|
|
1727
1822
|
const { attribute, callback } = options;
|
|
1728
1823
|
const value = getObjectAttribute(this.config, attribute);
|
|
1824
|
+
// `encryptedNative` says which attributes are stored encrypted, and the start decrypted exactly
|
|
1825
|
+
// those in place - decrypting again cannot be detected, because a value carries no mark saying so
|
|
1826
|
+
if ((Array.isArray(value) || typeof value === 'string') &&
|
|
1827
|
+
this._decryptedNativeAttributes.includes(attribute)) {
|
|
1828
|
+
return tools.maybeCallbackWithError(callback, null, value);
|
|
1829
|
+
}
|
|
1729
1830
|
if (Array.isArray(value)) {
|
|
1730
1831
|
const secret = await this.getSystemSecret();
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
if (typeof value[i] === 'string') {
|
|
1734
|
-
result[i] = tools.decrypt(secret, value[i]);
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1832
|
+
// entries that are not a string (e.g. an element without this attribute) are returned as they are
|
|
1833
|
+
const result = value.map(item => (typeof item === 'string' ? tools.decrypt(secret, item) : item));
|
|
1737
1834
|
return tools.maybeCallbackWithError(callback, null, result);
|
|
1738
1835
|
}
|
|
1739
1836
|
else if (typeof value === 'string') {
|
|
@@ -2120,7 +2217,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
2120
2217
|
* }
|
|
2121
2218
|
* ```
|
|
2122
2219
|
*
|
|
2123
|
-
* If following object will be passed as argument
|
|
2220
|
+
* If the following object will be passed as an argument
|
|
2124
2221
|
*
|
|
2125
2222
|
* ```js
|
|
2126
2223
|
* {
|
|
@@ -2231,36 +2328,29 @@ export class AdapterClass extends EventEmitter {
|
|
|
2231
2328
|
this._logger.error(`${this.namespaceLog} Object ${options.id} not exist!`);
|
|
2232
2329
|
oldObj = {};
|
|
2233
2330
|
}
|
|
2234
|
-
if (options.obj.native &&
|
|
2235
|
-
|
|
2236
|
-
oldObj.native &&
|
|
2237
|
-
oldObj.native.repositories) {
|
|
2238
|
-
oldObj.native.repositories = [];
|
|
2331
|
+
if (options.obj.native && 'repositories' in options.obj.native && oldObj.native?.repositories) {
|
|
2332
|
+
oldObj.native.repositories = {};
|
|
2239
2333
|
}
|
|
2240
|
-
if (options.obj.common && 'members' in options.obj.common && oldObj.common
|
|
2334
|
+
if (options.obj.common && 'members' in options.obj.common && oldObj.common?.members) {
|
|
2241
2335
|
oldObj.common.members = [];
|
|
2242
2336
|
}
|
|
2243
|
-
if (options.obj.native &&
|
|
2244
|
-
|
|
2245
|
-
oldObj.native &&
|
|
2246
|
-
oldObj.native.certificates) {
|
|
2247
|
-
oldObj.native.certificates = [];
|
|
2337
|
+
if (options.obj.native && 'certificates' in options.obj.native && oldObj.native?.certificates) {
|
|
2338
|
+
oldObj.native.certificates = {};
|
|
2248
2339
|
}
|
|
2249
|
-
if (options.obj.native && 'devices' in options.obj.native && oldObj.native
|
|
2340
|
+
if (options.obj.native && 'devices' in options.obj.native && oldObj.native?.devices) {
|
|
2250
2341
|
oldObj.native.devices = [];
|
|
2251
2342
|
}
|
|
2252
|
-
options.obj.from
|
|
2253
|
-
options.obj.user
|
|
2254
|
-
options.obj.ts
|
|
2343
|
+
options.obj.from ||= `system.adapter.${this.namespace}`;
|
|
2344
|
+
options.obj.user ||= (options.options ? options.options.user : '') || SYSTEM_ADMIN_USER;
|
|
2345
|
+
options.obj.ts ||= Date.now();
|
|
2255
2346
|
options.obj = extend(true, oldObj, options.obj);
|
|
2256
2347
|
// @ts-expect-error TODO we are returning type Object for ease of use to devs, but formally these are AnyObjects, e.g. not guaranteed to have common
|
|
2257
2348
|
return this.#objects.setObject(options.id, options.obj, options.options, options.callback);
|
|
2258
2349
|
}
|
|
2259
|
-
options.obj.from
|
|
2260
|
-
options.obj.user
|
|
2261
|
-
options.obj.ts
|
|
2262
|
-
if (
|
|
2263
|
-
(!options.obj.type && oldObj && oldObj.type === 'state')) {
|
|
2350
|
+
options.obj.from ||= `system.adapter.${this.namespace}`;
|
|
2351
|
+
options.obj.user ||= (options.options ? options.options.user : '') || SYSTEM_ADMIN_USER;
|
|
2352
|
+
options.obj.ts ||= Date.now();
|
|
2353
|
+
if (options.obj?.type === 'state' || (!options.obj.type && oldObj?.type === 'state')) {
|
|
2264
2354
|
if (options.obj.common &&
|
|
2265
2355
|
'custom' in options.obj.common &&
|
|
2266
2356
|
options.obj.common.custom !== null &&
|
|
@@ -2276,17 +2366,17 @@ export class AdapterClass extends EventEmitter {
|
|
|
2276
2366
|
}
|
|
2277
2367
|
}
|
|
2278
2368
|
if (!oldObj) {
|
|
2279
|
-
// if old object is not existing we behave like setObject
|
|
2369
|
+
// if old object is not existing, we behave like setObject
|
|
2280
2370
|
return this.setForeignObject(options.id, options.obj, options.options, options.callback);
|
|
2281
2371
|
}
|
|
2282
2372
|
try {
|
|
2283
|
-
const cbObj = await this.#objects.
|
|
2373
|
+
const cbObj = await this.#objects.extendObject(options.id, options.obj, options.options);
|
|
2284
2374
|
let defState;
|
|
2285
2375
|
if (options.obj.type === 'state' || oldObj.type === 'state') {
|
|
2286
2376
|
if (options.obj.common && 'def' in options.obj.common && options.obj.common.def !== undefined) {
|
|
2287
2377
|
defState = options.obj.common.def;
|
|
2288
2378
|
}
|
|
2289
|
-
else if (oldObj.common
|
|
2379
|
+
else if (oldObj.common?.def !== undefined) {
|
|
2290
2380
|
defState = oldObj.common.def;
|
|
2291
2381
|
}
|
|
2292
2382
|
}
|
|
@@ -2464,29 +2554,32 @@ export class AdapterClass extends EventEmitter {
|
|
|
2464
2554
|
this._logger.error(`${this.namespaceLog} Object ${id} not exist!`);
|
|
2465
2555
|
oldObj = {};
|
|
2466
2556
|
}
|
|
2467
|
-
if (obj.native &&
|
|
2468
|
-
|
|
2557
|
+
if (obj.native &&
|
|
2558
|
+
'repositories' in obj.native &&
|
|
2559
|
+
oldObj.native?.repositories) {
|
|
2560
|
+
oldObj.native.repositories = {};
|
|
2469
2561
|
}
|
|
2470
|
-
if (obj.common && 'members' in obj.common && oldObj.common
|
|
2562
|
+
if (obj.common && 'members' in obj.common && oldObj.common?.members) {
|
|
2471
2563
|
oldObj.common.members = [];
|
|
2472
2564
|
}
|
|
2473
|
-
if (obj.native && 'certificates' in obj.native && oldObj.native
|
|
2474
|
-
oldObj.native.certificates =
|
|
2565
|
+
if (obj.native && 'certificates' in obj.native && oldObj.native?.certificates) {
|
|
2566
|
+
oldObj.native.certificates = {};
|
|
2475
2567
|
}
|
|
2476
|
-
if (obj.native && 'devices' in obj.native && oldObj.native
|
|
2568
|
+
if (obj.native && 'devices' in obj.native && oldObj.native?.devices) {
|
|
2477
2569
|
oldObj.native.devices = [];
|
|
2478
2570
|
}
|
|
2479
|
-
obj.from
|
|
2480
|
-
obj.user
|
|
2481
|
-
obj.ts
|
|
2571
|
+
obj.from ||= `system.adapter.${this.namespace}`;
|
|
2572
|
+
obj.user ||= options?.user || SYSTEM_ADMIN_USER;
|
|
2573
|
+
obj.ts ||= Date.now();
|
|
2482
2574
|
obj = extend(true, oldObj, obj);
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
obj.
|
|
2488
|
-
obj.
|
|
2489
|
-
|
|
2575
|
+
return callback
|
|
2576
|
+
? this.#objects.setObject(id, obj, options, callback)
|
|
2577
|
+
: this.#objects.setObject(id, obj, options);
|
|
2578
|
+
}
|
|
2579
|
+
obj.from ||= `system.adapter.${this.namespace}`;
|
|
2580
|
+
obj.user ||= options?.user || SYSTEM_ADMIN_USER;
|
|
2581
|
+
obj.ts ||= Date.now();
|
|
2582
|
+
if ((obj.type && obj.type === 'state') || (!obj.type && oldObj?.type === 'state')) {
|
|
2490
2583
|
if (obj.common &&
|
|
2491
2584
|
'custom' in obj.common &&
|
|
2492
2585
|
obj.common.custom !== null &&
|
|
@@ -2506,13 +2599,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
2506
2599
|
return this.setForeignObject(id, obj, options, callback);
|
|
2507
2600
|
}
|
|
2508
2601
|
try {
|
|
2509
|
-
const cbObj = await this.#objects.
|
|
2602
|
+
const cbObj = await this.#objects.extendObject(id, obj, options);
|
|
2510
2603
|
if (cbObj?.value.type === 'state') {
|
|
2511
2604
|
let defState;
|
|
2512
|
-
if (obj.common && 'def' in obj.common && obj.common
|
|
2605
|
+
if (obj.common && 'def' in obj.common && obj.common?.def !== undefined) {
|
|
2513
2606
|
defState = obj.common.def;
|
|
2514
2607
|
}
|
|
2515
|
-
else if (oldObj.common
|
|
2608
|
+
else if (oldObj.common?.def !== undefined) {
|
|
2516
2609
|
defState = oldObj.common.def;
|
|
2517
2610
|
}
|
|
2518
2611
|
if (defState !== undefined) {
|
|
@@ -2650,7 +2743,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
2650
2743
|
Validator.assertString(design, 'design');
|
|
2651
2744
|
Validator.assertString(search, 'search');
|
|
2652
2745
|
Validator.assertOptionalCallback(callback, 'callback');
|
|
2653
|
-
params
|
|
2746
|
+
params ||= {};
|
|
2654
2747
|
Validator.assertObject(params, 'params');
|
|
2655
2748
|
if (options !== null && options !== undefined) {
|
|
2656
2749
|
Validator.assertObject(options, 'options');
|
|
@@ -2919,9 +3012,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
2919
3012
|
if (!parts[2]) {
|
|
2920
3013
|
continue;
|
|
2921
3014
|
}
|
|
2922
|
-
|
|
2923
|
-
result[`${parts[0]}.${parts[1]}`] = {};
|
|
2924
|
-
}
|
|
3015
|
+
result[`${parts[0]}.${parts[1]}`] ||= {};
|
|
2925
3016
|
result[`${parts[0]}.${parts[1]}`][row.id] = row.value;
|
|
2926
3017
|
}
|
|
2927
3018
|
}
|
|
@@ -3146,7 +3237,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
3146
3237
|
* @param options optional user context with language
|
|
3147
3238
|
* @param options.user current user
|
|
3148
3239
|
* @param options.language language in which the search must be done for multi-language names
|
|
3149
|
-
* @
|
|
3240
|
+
* @returns if the object was found by ID it will return id and may be the multi-language name it exists. If the object was found by name it will return id and the multi-language name. If the object was not found, it will return only name that was searched for.
|
|
3150
3241
|
*/
|
|
3151
3242
|
findForeignObjectAsync(id, type, options) {
|
|
3152
3243
|
return new Promise((resolve, reject) => this.findForeignObject(id, type, options || {}, (err, id, name) => {
|
|
@@ -3188,7 +3279,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
3188
3279
|
catch (e) {
|
|
3189
3280
|
return tools.maybeCallbackWithError(callback, e);
|
|
3190
3281
|
}
|
|
3191
|
-
return this._getForeignObject({ id, options, callback });
|
|
3282
|
+
return this._getForeignObject({ id: id, options, callback });
|
|
3192
3283
|
}
|
|
3193
3284
|
async _getForeignObject(options) {
|
|
3194
3285
|
if (!this.#objects) {
|
|
@@ -3196,14 +3287,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
3196
3287
|
return tools.maybeCallbackWithError(options.callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
3197
3288
|
}
|
|
3198
3289
|
try {
|
|
3199
|
-
const obj = await this.#objects.getObjectAsync(options.id, options);
|
|
3290
|
+
const obj = await this.#objects.getObjectAsync(options.id, options.options);
|
|
3200
3291
|
// remove protectedNative if not admin, not cloud or not own adapter
|
|
3201
|
-
if (obj &&
|
|
3292
|
+
if (obj?.native &&
|
|
3202
3293
|
'protectedNative' in obj &&
|
|
3203
3294
|
Array.isArray(obj.protectedNative) &&
|
|
3204
|
-
obj._id &&
|
|
3205
|
-
obj._id.startsWith('system.adapter.') &&
|
|
3206
|
-
obj.native &&
|
|
3295
|
+
obj._id?.startsWith('system.adapter.') &&
|
|
3207
3296
|
!NO_PROTECT_ADAPTERS.includes(this.name) &&
|
|
3208
3297
|
this.name !== obj._id.split('.')[2]) {
|
|
3209
3298
|
for (const attr of obj.protectedNative) {
|
|
@@ -3240,31 +3329,35 @@ export class AdapterClass extends EventEmitter {
|
|
|
3240
3329
|
// @ts-expect-error we have ensured that it is string for the rest the method will validate again
|
|
3241
3330
|
this.delForeignObject(id, options, callback);
|
|
3242
3331
|
}
|
|
3243
|
-
_deleteObjects(tasks, options, cb) {
|
|
3244
|
-
if (!tasks
|
|
3332
|
+
async _deleteObjects(tasks, options, cb) {
|
|
3333
|
+
if (!tasks?.length) {
|
|
3245
3334
|
return tools.maybeCallback(cb);
|
|
3246
3335
|
}
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3336
|
+
try {
|
|
3337
|
+
for (const task of tasks) {
|
|
3338
|
+
await new Promise((resolve, reject) => this.#objects.delObject(task.id, options, err => {
|
|
3339
|
+
err ? reject(err) : resolve();
|
|
3340
|
+
}));
|
|
3341
|
+
if (task.state) {
|
|
3342
|
+
try {
|
|
3343
|
+
await this.delForeignStateAsync(task.id, options);
|
|
3344
|
+
}
|
|
3345
|
+
catch (e) {
|
|
3346
|
+
this._logger.warn(`${this.namespaceLog} Could not remove state of ${task.id}: ${e.message}`);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3253
3349
|
try {
|
|
3254
|
-
await
|
|
3350
|
+
await tools.removeIdFromAllEnums(this.#objects, task.id, this.enums);
|
|
3255
3351
|
}
|
|
3256
3352
|
catch (e) {
|
|
3257
|
-
this._logger.warn(`${this.namespaceLog} Could not remove
|
|
3353
|
+
this._logger.warn(`${this.namespaceLog} Could not remove ${task.id} from enums: ${e.message}`);
|
|
3258
3354
|
}
|
|
3259
3355
|
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
}
|
|
3266
|
-
setImmediate(() => this._deleteObjects(tasks, options, cb));
|
|
3267
|
-
});
|
|
3356
|
+
}
|
|
3357
|
+
catch (err) {
|
|
3358
|
+
return tools.maybeCallbackWithError(cb, err);
|
|
3359
|
+
}
|
|
3360
|
+
return tools.maybeCallback(cb);
|
|
3268
3361
|
}
|
|
3269
3362
|
/**
|
|
3270
3363
|
* Delete any object.
|
|
@@ -3316,11 +3409,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
3316
3409
|
const selector = { startkey: `${id}.`, endkey: `${id}.\u9999` };
|
|
3317
3410
|
// read all underlying states
|
|
3318
3411
|
this.#objects.getObjectList(selector, options, (err, res) => {
|
|
3319
|
-
res &&
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
this._deleteObjects(tasks, options, callback);
|
|
3412
|
+
res?.rows.forEach((item) => !tasks.find(task => task.id === item.id) &&
|
|
3413
|
+
!item.value?.common?.dontDelete && // exclude objects with dontDelete flag
|
|
3414
|
+
tasks.push({ id: item.id, state: item.value?.type === 'state' }));
|
|
3415
|
+
return this._deleteObjects(tasks, options, callback);
|
|
3324
3416
|
});
|
|
3325
3417
|
});
|
|
3326
3418
|
}
|
|
@@ -3329,7 +3421,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
3329
3421
|
if (err) {
|
|
3330
3422
|
return tools.maybeCallbackWithError(callback, err);
|
|
3331
3423
|
}
|
|
3332
|
-
|
|
3424
|
+
if (obj) {
|
|
3333
3425
|
// do not allow deletion of objects with dontDelete flag
|
|
3334
3426
|
if (obj.common?.dontDelete) {
|
|
3335
3427
|
return tools.maybeCallbackWithError(callback, new Error('not deletable'));
|
|
@@ -3381,17 +3473,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
3381
3473
|
if (options !== null && options !== undefined) {
|
|
3382
3474
|
Validator.assertObject(options, 'options');
|
|
3383
3475
|
}
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
if (pattern === '*') {
|
|
3389
|
-
this.#objects.subscribeUser(`${this.namespace}.*`, options, callback);
|
|
3390
|
-
}
|
|
3391
|
-
else {
|
|
3392
|
-
const fixedPattern = Array.isArray(pattern) ? pattern : this._utils.fixId(pattern, true);
|
|
3393
|
-
this.#objects.subscribeUser(fixedPattern, options, callback);
|
|
3394
|
-
}
|
|
3476
|
+
const cb = callback;
|
|
3477
|
+
this.#subscriptions
|
|
3478
|
+
.subscribeObjects(pattern, options)
|
|
3479
|
+
.then(() => cb?.(), (err) => cb?.(err));
|
|
3395
3480
|
}
|
|
3396
3481
|
/**
|
|
3397
3482
|
* Unsubscribe on the changes of objects in this instance.
|
|
@@ -3415,17 +3500,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
3415
3500
|
if (options !== null && options !== undefined) {
|
|
3416
3501
|
Validator.assertObject(options, 'options');
|
|
3417
3502
|
}
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
if (pattern === '*') {
|
|
3423
|
-
this.#objects.unsubscribeUser(`${this.namespace}.*`, options, callback);
|
|
3424
|
-
}
|
|
3425
|
-
else {
|
|
3426
|
-
const fixedPattern = Array.isArray(pattern) ? pattern : this._utils.fixId(pattern, true);
|
|
3427
|
-
this.#objects.unsubscribeUser(fixedPattern, options, callback);
|
|
3428
|
-
}
|
|
3503
|
+
const cb = callback;
|
|
3504
|
+
this.#subscriptions
|
|
3505
|
+
.unsubscribeObjects(pattern, options)
|
|
3506
|
+
.then(() => cb?.(), (err) => cb?.(err));
|
|
3429
3507
|
}
|
|
3430
3508
|
/**
|
|
3431
3509
|
* Subscribe for the changes of objects in any instance.
|
|
@@ -3449,11 +3527,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
3449
3527
|
if (options !== null && options !== undefined) {
|
|
3450
3528
|
Validator.assertObject(options, 'options');
|
|
3451
3529
|
}
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
this.#objects.subscribeUser(pattern, options, callback);
|
|
3530
|
+
const cb = callback;
|
|
3531
|
+
this.#subscriptions
|
|
3532
|
+
.subscribeForeignObjects(pattern, options)
|
|
3533
|
+
.then(() => cb?.(), (err) => cb?.(err));
|
|
3457
3534
|
}
|
|
3458
3535
|
/**
|
|
3459
3536
|
* Unsubscribe for the patterns on all objects.
|
|
@@ -3480,11 +3557,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
3480
3557
|
if (options !== null && options !== undefined) {
|
|
3481
3558
|
Validator.assertObject(options, 'options');
|
|
3482
3559
|
}
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
this.#objects.unsubscribeUser(pattern, options, callback);
|
|
3560
|
+
const cb = callback;
|
|
3561
|
+
this.#subscriptions
|
|
3562
|
+
.unsubscribeForeignObjects(pattern, options)
|
|
3563
|
+
.then(() => cb?.(), (err) => cb?.(err));
|
|
3488
3564
|
}
|
|
3489
3565
|
/**
|
|
3490
3566
|
* Subscribe for the changes of files in specific instance.
|
|
@@ -3494,16 +3570,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
3494
3570
|
* @param options optional user context
|
|
3495
3571
|
*/
|
|
3496
3572
|
subscribeForeignFiles(id, pattern, options) {
|
|
3497
|
-
if (!this.#objects) {
|
|
3498
|
-
this._logger.info(`${this.namespaceLog} subscribeForeignFiles not processed because Objects database not connected`);
|
|
3499
|
-
throw new Error(tools.ERRORS.ERROR_DB_CLOSED);
|
|
3500
|
-
}
|
|
3501
3573
|
Validator.assertString(id, 'id');
|
|
3502
3574
|
Validator.assertPattern(pattern, 'pattern');
|
|
3503
3575
|
if (options !== null && options !== undefined) {
|
|
3504
3576
|
Validator.assertObject(options, 'options');
|
|
3505
3577
|
}
|
|
3506
|
-
return this.#
|
|
3578
|
+
return this.#subscriptions.subscribeForeignFiles(id, pattern, options);
|
|
3507
3579
|
}
|
|
3508
3580
|
/**
|
|
3509
3581
|
* Unsubscribe for the changes of files on specific instance.
|
|
@@ -3516,16 +3588,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
3516
3588
|
if (!pattern) {
|
|
3517
3589
|
pattern = '*';
|
|
3518
3590
|
}
|
|
3519
|
-
if (!this.#objects) {
|
|
3520
|
-
this._logger.info(`${this.namespaceLog} unsubscribeForeignFiles not processed because Objects database not connected`);
|
|
3521
|
-
throw new Error(tools.ERRORS.ERROR_DB_CLOSED);
|
|
3522
|
-
}
|
|
3523
3591
|
Validator.assertString(id, 'id');
|
|
3524
3592
|
Validator.assertPattern(pattern, 'pattern');
|
|
3525
3593
|
if (options !== null && options !== undefined) {
|
|
3526
3594
|
Validator.assertObject(options, 'options');
|
|
3527
3595
|
}
|
|
3528
|
-
return this.#
|
|
3596
|
+
return this.#subscriptions.unsubscribeForeignFiles(id, pattern, options);
|
|
3529
3597
|
}
|
|
3530
3598
|
/**
|
|
3531
3599
|
* Same as {@link AdapterClass.setObject}, but with check if the object exists.
|
|
@@ -3680,11 +3748,11 @@ export class AdapterClass extends EventEmitter {
|
|
|
3680
3748
|
return id;
|
|
3681
3749
|
}
|
|
3682
3750
|
/**
|
|
3683
|
-
* @param deviceName
|
|
3684
|
-
* @param common
|
|
3685
|
-
* @param _native
|
|
3686
|
-
* @param options
|
|
3687
|
-
* @param callback
|
|
3751
|
+
* @param deviceName the name of the device
|
|
3752
|
+
* @param common the common section of the device object
|
|
3753
|
+
* @param _native the native section of the device object
|
|
3754
|
+
* @param options optional user context
|
|
3755
|
+
* @param callback return result
|
|
3688
3756
|
* @deprecated use `this.extendObject` instead
|
|
3689
3757
|
*/
|
|
3690
3758
|
createDevice(deviceName, common, _native, options, callback) {
|
|
@@ -3715,31 +3783,34 @@ export class AdapterClass extends EventEmitter {
|
|
|
3715
3783
|
deviceName,
|
|
3716
3784
|
_native,
|
|
3717
3785
|
callback,
|
|
3718
|
-
options,
|
|
3786
|
+
options: options,
|
|
3719
3787
|
});
|
|
3720
3788
|
}
|
|
3721
3789
|
_createDevice(_options) {
|
|
3722
3790
|
let { common, deviceName, _native } = _options;
|
|
3723
3791
|
const { callback, options } = _options;
|
|
3724
|
-
common
|
|
3725
|
-
common.name
|
|
3792
|
+
common ||= {};
|
|
3793
|
+
common.name ||= deviceName;
|
|
3726
3794
|
deviceName = deviceName.replace(FORBIDDEN_CHARS, '_').replace(/\./g, '_');
|
|
3727
3795
|
_native = _native || {};
|
|
3728
|
-
this.setObjectNotExists(deviceName, {
|
|
3796
|
+
const createResult = this.setObjectNotExists(deviceName, {
|
|
3729
3797
|
type: 'device',
|
|
3730
3798
|
common: common,
|
|
3731
3799
|
native: _native,
|
|
3732
3800
|
}, options, callback);
|
|
3801
|
+
if (createResult instanceof Promise) {
|
|
3802
|
+
createResult.catch(e => this._logger.error(`${this.namespaceLog} Cannot create device: ${e.message}`));
|
|
3803
|
+
}
|
|
3733
3804
|
}
|
|
3734
3805
|
/**
|
|
3735
3806
|
* Name of channel must be in format "channel"
|
|
3736
3807
|
*
|
|
3737
|
-
* @param parentDevice
|
|
3738
|
-
* @param channelName
|
|
3739
|
-
* @param roleOrCommon
|
|
3740
|
-
* @param _native
|
|
3741
|
-
* @param options
|
|
3742
|
-
* @param callback
|
|
3808
|
+
* @param parentDevice the parent device name
|
|
3809
|
+
* @param channelName the name of the channel
|
|
3810
|
+
* @param roleOrCommon role string or the common section of the channel object
|
|
3811
|
+
* @param _native the native section of the channel object
|
|
3812
|
+
* @param options optional user context
|
|
3813
|
+
* @param callback return result
|
|
3743
3814
|
* @deprecated use `this.extendObject` instead
|
|
3744
3815
|
*/
|
|
3745
3816
|
createChannel(parentDevice, channelName, roleOrCommon, _native, options, callback) {
|
|
@@ -3773,29 +3844,30 @@ export class AdapterClass extends EventEmitter {
|
|
|
3773
3844
|
Validator.assertString(channelName, 'channelName');
|
|
3774
3845
|
Validator.assertString(parentDevice, 'parentDevice');
|
|
3775
3846
|
Validator.assertOptionalCallback(callback, 'callback');
|
|
3776
|
-
common.name
|
|
3847
|
+
common.name ||= channelName;
|
|
3777
3848
|
if (parentDevice) {
|
|
3778
3849
|
parentDevice = parentDevice.replace(FORBIDDEN_CHARS, '_').replace(/\./g, '_');
|
|
3779
3850
|
}
|
|
3780
3851
|
channelName = channelName.replace(FORBIDDEN_CHARS, '_').replace(/\./g, '_');
|
|
3781
3852
|
// @ts-expect-error ts somehow loses types here
|
|
3782
3853
|
channelName = this._DCS2ID(parentDevice, channelName);
|
|
3783
|
-
_native
|
|
3854
|
+
_native ||= {};
|
|
3784
3855
|
const obj = {
|
|
3856
|
+
_id: channelName,
|
|
3785
3857
|
type: 'channel',
|
|
3786
|
-
common
|
|
3858
|
+
common,
|
|
3787
3859
|
native: _native,
|
|
3788
3860
|
};
|
|
3789
|
-
this.setObjectNotExists(channelName, obj, options, callback);
|
|
3861
|
+
return this.setObjectNotExists(channelName, obj, options, callback);
|
|
3790
3862
|
}
|
|
3791
3863
|
/**
|
|
3792
|
-
* @param parentDevice
|
|
3793
|
-
* @param parentChannel
|
|
3794
|
-
* @param stateName
|
|
3795
|
-
* @param roleOrCommon
|
|
3796
|
-
* @param _native
|
|
3797
|
-
* @param options
|
|
3798
|
-
* @param callback
|
|
3864
|
+
* @param parentDevice the parent device name
|
|
3865
|
+
* @param parentChannel the parent channel name
|
|
3866
|
+
* @param stateName the name of the state
|
|
3867
|
+
* @param roleOrCommon role string or the common section of the state object
|
|
3868
|
+
* @param _native the native section of the state object
|
|
3869
|
+
* @param options optional user context
|
|
3870
|
+
* @param callback return result
|
|
3799
3871
|
* @deprecated use `this.extendObject` instead
|
|
3800
3872
|
*/
|
|
3801
3873
|
createState(parentDevice, parentChannel, stateName, roleOrCommon, _native, options, callback) {
|
|
@@ -3822,12 +3894,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
3822
3894
|
write: false,
|
|
3823
3895
|
name: '',
|
|
3824
3896
|
role: roleOrCommon,
|
|
3897
|
+
type: 'mixed',
|
|
3825
3898
|
};
|
|
3826
3899
|
}
|
|
3827
3900
|
else if (tools.isObject(roleOrCommon)) {
|
|
3828
3901
|
common = roleOrCommon;
|
|
3829
3902
|
}
|
|
3830
|
-
_native
|
|
3903
|
+
_native ||= {};
|
|
3831
3904
|
Validator.assertObject(common, 'common');
|
|
3832
3905
|
Validator.assertString(stateName, 'stateName');
|
|
3833
3906
|
Validator.assertString(parentDevice, 'parentDevice');
|
|
@@ -3842,9 +3915,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
3842
3915
|
_createState(_options) {
|
|
3843
3916
|
const { _native, common, callback, options } = _options;
|
|
3844
3917
|
let { parentChannel, parentDevice, stateName } = _options;
|
|
3845
|
-
common.name
|
|
3846
|
-
common.read
|
|
3847
|
-
common.write
|
|
3918
|
+
common.name ||= stateName;
|
|
3919
|
+
common.read ??= true;
|
|
3920
|
+
common.write ??= false;
|
|
3848
3921
|
if (!common.role) {
|
|
3849
3922
|
this._logger.error(`${this.namespaceLog} Try to create state ${parentDevice ? `${parentDevice}.` : ''}${parentChannel}.${stateName} without role`);
|
|
3850
3923
|
return;
|
|
@@ -3914,7 +3987,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
3914
3987
|
common.def = max;
|
|
3915
3988
|
}
|
|
3916
3989
|
}
|
|
3917
|
-
this.setObjectNotExists(id, {
|
|
3990
|
+
const stateResult = this.setObjectNotExists(id, {
|
|
3918
3991
|
type: 'state',
|
|
3919
3992
|
common: common,
|
|
3920
3993
|
native: _native,
|
|
@@ -3922,32 +3995,29 @@ export class AdapterClass extends EventEmitter {
|
|
|
3922
3995
|
if (err) {
|
|
3923
3996
|
return tools.maybeCallbackWithError(callback, err);
|
|
3924
3997
|
}
|
|
3925
|
-
|
|
3998
|
+
if (common.def !== undefined) {
|
|
3926
3999
|
this.getState(id, null, (err, state) => {
|
|
3927
4000
|
if (!state) {
|
|
3928
4001
|
if (common.defAck !== undefined) {
|
|
3929
|
-
this.setState(id, common.def, common.defAck, options, callback);
|
|
4002
|
+
return this.setState(id, common.def, common.defAck, options, callback);
|
|
3930
4003
|
}
|
|
3931
|
-
|
|
3932
|
-
this.setState(id, common.def, options, callback);
|
|
3933
|
-
}
|
|
3934
|
-
}
|
|
3935
|
-
else {
|
|
3936
|
-
return tools.maybeCallback(callback);
|
|
4004
|
+
return this.setState(id, common.def, options, callback);
|
|
3937
4005
|
}
|
|
4006
|
+
return tools.maybeCallback(callback);
|
|
3938
4007
|
});
|
|
3939
4008
|
}
|
|
3940
4009
|
else {
|
|
3941
4010
|
this.getState(id, null, (err, state) => {
|
|
3942
4011
|
if (!state) {
|
|
3943
|
-
this.setState(id, null, true, options, callback);
|
|
3944
|
-
}
|
|
3945
|
-
else {
|
|
3946
|
-
return tools.maybeCallback(callback);
|
|
4012
|
+
return this.setState(id, null, true, options, callback);
|
|
3947
4013
|
}
|
|
4014
|
+
return tools.maybeCallback(callback);
|
|
3948
4015
|
});
|
|
3949
4016
|
}
|
|
3950
4017
|
});
|
|
4018
|
+
if (stateResult instanceof Promise) {
|
|
4019
|
+
stateResult.catch(e => this._logger.error(`${this.namespaceLog} Cannot create state object: ${e.message}`));
|
|
4020
|
+
}
|
|
3951
4021
|
}
|
|
3952
4022
|
/**
|
|
3953
4023
|
* Delete device with all its channels and states.
|
|
@@ -4005,6 +4075,16 @@ export class AdapterClass extends EventEmitter {
|
|
|
4005
4075
|
}
|
|
4006
4076
|
return tools.maybeCallback(callback);
|
|
4007
4077
|
}
|
|
4078
|
+
/**
|
|
4079
|
+
* Add a channel to an enum.
|
|
4080
|
+
*
|
|
4081
|
+
* @param enumName the enum category (e.g. rooms, functions)
|
|
4082
|
+
* @param addTo the enum id to add the channel to
|
|
4083
|
+
* @param parentDevice the parent device name
|
|
4084
|
+
* @param channelName the name of the channel
|
|
4085
|
+
* @param options optional user context
|
|
4086
|
+
* @param callback return result
|
|
4087
|
+
*/
|
|
4008
4088
|
addChannelToEnum(enumName, addTo, parentDevice, channelName, options, callback) {
|
|
4009
4089
|
if (typeof options === 'function') {
|
|
4010
4090
|
callback = options;
|
|
@@ -4052,11 +4132,11 @@ export class AdapterClass extends EventEmitter {
|
|
|
4052
4132
|
obj.from = `system.adapter.${this.namespace}`;
|
|
4053
4133
|
obj.user = options?.user || SYSTEM_ADMIN_USER;
|
|
4054
4134
|
obj.ts = Date.now();
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
return tools.maybeCallback(callback);
|
|
4135
|
+
return callback
|
|
4136
|
+
? this.#objects.setObject(obj._id, obj, options, callback)
|
|
4137
|
+
: this.#objects.setObject(obj._id, obj, options);
|
|
4059
4138
|
}
|
|
4139
|
+
return tools.maybeCallback(callback);
|
|
4060
4140
|
}
|
|
4061
4141
|
});
|
|
4062
4142
|
}
|
|
@@ -4076,28 +4156,40 @@ export class AdapterClass extends EventEmitter {
|
|
|
4076
4156
|
obj.from = `system.adapter.${this.namespace}`;
|
|
4077
4157
|
obj.user = options?.user || SYSTEM_ADMIN_USER;
|
|
4078
4158
|
obj.ts = Date.now();
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
return tools.maybeCallback(callback);
|
|
4159
|
+
return callback
|
|
4160
|
+
? this.#objects.setObject(obj._id, obj, options, callback)
|
|
4161
|
+
: this.#objects.setObject(obj._id, obj, options);
|
|
4083
4162
|
}
|
|
4163
|
+
return tools.maybeCallback(callback);
|
|
4084
4164
|
}
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
this.#objects.setObject(`enum.${enumName}.${addTo}`, {
|
|
4088
|
-
common: {
|
|
4089
|
-
name: addTo,
|
|
4090
|
-
members: [objId],
|
|
4091
|
-
},
|
|
4165
|
+
// Create enum
|
|
4166
|
+
return callback
|
|
4167
|
+
? this.#objects.setObject(`enum.${enumName}.${addTo}`, {
|
|
4168
|
+
common: { name: addTo, members: [objId] },
|
|
4092
4169
|
from: `system.adapter.${this.namespace}`,
|
|
4093
4170
|
ts: Date.now(),
|
|
4094
4171
|
type: 'enum',
|
|
4095
4172
|
native: {},
|
|
4096
|
-
}, options, callback)
|
|
4097
|
-
|
|
4173
|
+
}, options, callback)
|
|
4174
|
+
: this.#objects.setObject(`enum.${enumName}.${addTo}`, {
|
|
4175
|
+
common: { name: addTo, members: [objId] },
|
|
4176
|
+
from: `system.adapter.${this.namespace}`,
|
|
4177
|
+
ts: Date.now(),
|
|
4178
|
+
type: 'enum',
|
|
4179
|
+
native: {},
|
|
4180
|
+
}, options);
|
|
4098
4181
|
});
|
|
4099
4182
|
}
|
|
4100
4183
|
}
|
|
4184
|
+
/**
|
|
4185
|
+
* Remove a channel from an enum.
|
|
4186
|
+
*
|
|
4187
|
+
* @param enumName the enum category (e.g. rooms, functions)
|
|
4188
|
+
* @param parentDevice the parent device name
|
|
4189
|
+
* @param channelName the name of the channel
|
|
4190
|
+
* @param options optional user context
|
|
4191
|
+
* @param callback return result
|
|
4192
|
+
*/
|
|
4101
4193
|
deleteChannelFromEnum(enumName, parentDevice, channelName, options, callback) {
|
|
4102
4194
|
if (typeof options === 'function') {
|
|
4103
4195
|
callback = options;
|
|
@@ -4258,11 +4350,11 @@ export class AdapterClass extends EventEmitter {
|
|
|
4258
4350
|
return tools.maybeCallback(callback);
|
|
4259
4351
|
}
|
|
4260
4352
|
/**
|
|
4261
|
-
* @param parentDevice
|
|
4262
|
-
* @param parentChannel
|
|
4263
|
-
* @param stateName
|
|
4264
|
-
* @param options
|
|
4265
|
-
* @param callback
|
|
4353
|
+
* @param parentDevice the parent device name
|
|
4354
|
+
* @param parentChannel the parent channel name
|
|
4355
|
+
* @param stateName the name of the state
|
|
4356
|
+
* @param options optional user context
|
|
4357
|
+
* @param callback return result
|
|
4266
4358
|
* @deprecated use `this.delObject` instead
|
|
4267
4359
|
*/
|
|
4268
4360
|
deleteState(parentDevice, parentChannel, stateName, options, callback) {
|
|
@@ -4346,6 +4438,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
4346
4438
|
const _name = this._DCS2ID(parentDevice, parentChannel, stateName);
|
|
4347
4439
|
this.delObject(_name, options, callback);
|
|
4348
4440
|
}
|
|
4441
|
+
/**
|
|
4442
|
+
* Get all devices of this adapter.
|
|
4443
|
+
*
|
|
4444
|
+
* @param options optional user context, or the callback
|
|
4445
|
+
* @param callback return result
|
|
4446
|
+
*/
|
|
4349
4447
|
getDevices(options, callback) {
|
|
4350
4448
|
if (typeof options === 'function' && typeof callback === 'object') {
|
|
4351
4449
|
const tmp = callback;
|
|
@@ -4384,6 +4482,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
4384
4482
|
return tools.maybeCallbackWithError(callback, null, res);
|
|
4385
4483
|
});
|
|
4386
4484
|
}
|
|
4485
|
+
/**
|
|
4486
|
+
* Get all channels of this adapter or of the given device.
|
|
4487
|
+
*
|
|
4488
|
+
* @param parentDevice the parent device name, or the callback
|
|
4489
|
+
* @param options optional user context, or the callback
|
|
4490
|
+
* @param callback return result
|
|
4491
|
+
*/
|
|
4387
4492
|
getChannelsOf(parentDevice, options, callback) {
|
|
4388
4493
|
if (typeof options === 'function') {
|
|
4389
4494
|
callback = options;
|
|
@@ -4431,6 +4536,14 @@ export class AdapterClass extends EventEmitter {
|
|
|
4431
4536
|
return tools.maybeCallbackWithError(options.callback, null, res);
|
|
4432
4537
|
});
|
|
4433
4538
|
}
|
|
4539
|
+
/**
|
|
4540
|
+
* Get all states of this adapter or of the given device/channel.
|
|
4541
|
+
*
|
|
4542
|
+
* @param parentDevice the parent device name, or the callback
|
|
4543
|
+
* @param parentChannel the parent channel name, or the callback
|
|
4544
|
+
* @param options optional user context, or the callback
|
|
4545
|
+
* @param callback return result
|
|
4546
|
+
*/
|
|
4434
4547
|
getStatesOf(parentDevice, parentChannel, options, callback) {
|
|
4435
4548
|
if (typeof options === 'function') {
|
|
4436
4549
|
callback = options;
|
|
@@ -4509,6 +4622,17 @@ export class AdapterClass extends EventEmitter {
|
|
|
4509
4622
|
}
|
|
4510
4623
|
});
|
|
4511
4624
|
}
|
|
4625
|
+
/**
|
|
4626
|
+
* Add a state to an enum.
|
|
4627
|
+
*
|
|
4628
|
+
* @param enumName the enum category (e.g. rooms, functions)
|
|
4629
|
+
* @param addTo the enum id to add the state to
|
|
4630
|
+
* @param parentDevice the parent device name
|
|
4631
|
+
* @param parentChannel the parent channel name
|
|
4632
|
+
* @param stateName the name of the state
|
|
4633
|
+
* @param options optional user context
|
|
4634
|
+
* @param callback return result
|
|
4635
|
+
*/
|
|
4512
4636
|
addStateToEnum(enumName, addTo, parentDevice, parentChannel, stateName, options, callback) {
|
|
4513
4637
|
if (typeof options === 'function') {
|
|
4514
4638
|
callback = options;
|
|
@@ -4568,11 +4692,11 @@ export class AdapterClass extends EventEmitter {
|
|
|
4568
4692
|
obj.from = `system.adapter.${this.namespace}`;
|
|
4569
4693
|
obj.user = options?.user || SYSTEM_ADMIN_USER;
|
|
4570
4694
|
obj.ts = Date.now();
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
return tools.maybeCallback(callback);
|
|
4695
|
+
return callback
|
|
4696
|
+
? this.#objects.setObject(obj._id, obj, options, callback)
|
|
4697
|
+
: this.#objects.setObject(obj._id, obj, options);
|
|
4575
4698
|
}
|
|
4699
|
+
return tools.maybeCallback(callback);
|
|
4576
4700
|
});
|
|
4577
4701
|
}
|
|
4578
4702
|
else {
|
|
@@ -4587,31 +4711,44 @@ export class AdapterClass extends EventEmitter {
|
|
|
4587
4711
|
obj.from = `system.adapter.${this.namespace}`;
|
|
4588
4712
|
obj.user = options?.user || SYSTEM_ADMIN_USER;
|
|
4589
4713
|
obj.ts = Date.now();
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
return tools.maybeCallback(callback);
|
|
4714
|
+
return callback
|
|
4715
|
+
? this.#objects.setObject(obj._id, obj, callback)
|
|
4716
|
+
: this.#objects.setObject(obj._id, obj);
|
|
4594
4717
|
}
|
|
4718
|
+
return tools.maybeCallback(callback);
|
|
4595
4719
|
}
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
this.#objects.setObject(`enum.${enumName}.${addTo}`, {
|
|
4602
|
-
common: {
|
|
4603
|
-
name: addTo,
|
|
4604
|
-
members: [objId],
|
|
4605
|
-
},
|
|
4720
|
+
if (err) {
|
|
4721
|
+
return tools.maybeCallbackWithError(callback, err);
|
|
4722
|
+
}
|
|
4723
|
+
// Create enum
|
|
4724
|
+
return callback
|
|
4725
|
+
? this.#objects.setObject(`enum.${enumName}.${addTo}`, {
|
|
4726
|
+
common: { name: addTo, members: [objId] },
|
|
4606
4727
|
from: `system.adapter.${this.namespace}`,
|
|
4607
4728
|
ts: Date.now(),
|
|
4608
4729
|
type: 'enum',
|
|
4609
4730
|
native: {},
|
|
4610
|
-
}, options, callback)
|
|
4611
|
-
|
|
4731
|
+
}, options, callback)
|
|
4732
|
+
: this.#objects.setObject(`enum.${enumName}.${addTo}`, {
|
|
4733
|
+
common: { name: addTo, members: [objId] },
|
|
4734
|
+
from: `system.adapter.${this.namespace}`,
|
|
4735
|
+
ts: Date.now(),
|
|
4736
|
+
type: 'enum',
|
|
4737
|
+
native: {},
|
|
4738
|
+
}, options);
|
|
4612
4739
|
});
|
|
4613
4740
|
}
|
|
4614
4741
|
}
|
|
4742
|
+
/**
|
|
4743
|
+
* Remove a state from an enum.
|
|
4744
|
+
*
|
|
4745
|
+
* @param enumName the enum category (e.g. rooms, functions)
|
|
4746
|
+
* @param parentDevice the parent device name
|
|
4747
|
+
* @param parentChannel the parent channel name
|
|
4748
|
+
* @param stateName the name of the state
|
|
4749
|
+
* @param options optional user context
|
|
4750
|
+
* @param callback return result
|
|
4751
|
+
*/
|
|
4615
4752
|
deleteStateFromEnum(enumName, parentDevice, parentChannel, stateName, options, callback) {
|
|
4616
4753
|
if (typeof options === 'function') {
|
|
4617
4754
|
callback = options;
|
|
@@ -4823,6 +4960,14 @@ export class AdapterClass extends EventEmitter {
|
|
|
4823
4960
|
}
|
|
4824
4961
|
this.#objects.readDir(_adapter, path, options, callback);
|
|
4825
4962
|
}
|
|
4963
|
+
/**
|
|
4964
|
+
* Deletes a file in the DB.
|
|
4965
|
+
*
|
|
4966
|
+
* @param _adapter adapter name, or null for the current adapter
|
|
4967
|
+
* @param name path to the file
|
|
4968
|
+
* @param options optional user context
|
|
4969
|
+
* @param callback return result
|
|
4970
|
+
*/
|
|
4826
4971
|
unlink(_adapter, name, options, callback) {
|
|
4827
4972
|
if (_adapter === null) {
|
|
4828
4973
|
_adapter = this.name;
|
|
@@ -4843,6 +4988,15 @@ export class AdapterClass extends EventEmitter {
|
|
|
4843
4988
|
}
|
|
4844
4989
|
this.#objects.unlink(_adapter, name, options, callback);
|
|
4845
4990
|
}
|
|
4991
|
+
/**
|
|
4992
|
+
* Renames a file in the DB.
|
|
4993
|
+
*
|
|
4994
|
+
* @param _adapter adapter name, or null for the current adapter
|
|
4995
|
+
* @param oldName current path to the file
|
|
4996
|
+
* @param newName new path to the file
|
|
4997
|
+
* @param options optional user context
|
|
4998
|
+
* @param callback return result
|
|
4999
|
+
*/
|
|
4846
5000
|
rename(_adapter, oldName, newName, options, callback) {
|
|
4847
5001
|
if (_adapter === null) {
|
|
4848
5002
|
_adapter = this.name;
|
|
@@ -4862,8 +5016,16 @@ export class AdapterClass extends EventEmitter {
|
|
|
4862
5016
|
this._logger.info(`${this.namespaceLog} rename not processed because Objects database not connected`);
|
|
4863
5017
|
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
4864
5018
|
}
|
|
4865
|
-
this.#objects.rename(_adapter, oldName, newName, options, callback);
|
|
5019
|
+
return this.#objects.rename(_adapter, oldName, newName, options, callback);
|
|
4866
5020
|
}
|
|
5021
|
+
/**
|
|
5022
|
+
* Creates a directory in the DB.
|
|
5023
|
+
*
|
|
5024
|
+
* @param _adapter adapter name, or null for the current adapter
|
|
5025
|
+
* @param dirname path to the directory
|
|
5026
|
+
* @param options optional user context
|
|
5027
|
+
* @param callback return result
|
|
5028
|
+
*/
|
|
4867
5029
|
mkdir(_adapter, dirname, options, callback) {
|
|
4868
5030
|
if (_adapter === null) {
|
|
4869
5031
|
_adapter = this.name;
|
|
@@ -5001,6 +5163,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
5001
5163
|
return tools.maybeCallbackWithError(callback, e);
|
|
5002
5164
|
}
|
|
5003
5165
|
}
|
|
5166
|
+
/**
|
|
5167
|
+
* Formats a numeric value using the given format string.
|
|
5168
|
+
*
|
|
5169
|
+
* @param value the value to format
|
|
5170
|
+
* @param decimals number of decimals to keep, or the format string
|
|
5171
|
+
* @param _format optional format string for the decimal and thousands separator
|
|
5172
|
+
*/
|
|
5004
5173
|
formatValue(value, decimals, _format) {
|
|
5005
5174
|
if (typeof decimals !== 'number') {
|
|
5006
5175
|
_format = decimals;
|
|
@@ -5030,6 +5199,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
5030
5199
|
// @ts-expect-error fix later
|
|
5031
5200
|
.replace(/\B(?=(\d{3})+(?!\d))/g, format[0]);
|
|
5032
5201
|
}
|
|
5202
|
+
/**
|
|
5203
|
+
* Formats a date or duration according to the given format string.
|
|
5204
|
+
*
|
|
5205
|
+
* @param dateObj the date to format
|
|
5206
|
+
* @param isDuration whether the value represents a duration, or the format string
|
|
5207
|
+
* @param _format optional format string
|
|
5208
|
+
*/
|
|
5033
5209
|
formatDate(dateObj, isDuration, _format) {
|
|
5034
5210
|
if ((typeof isDuration === 'string' && isDuration.toLowerCase() === 'duration') || isDuration === true) {
|
|
5035
5211
|
isDuration = true;
|
|
@@ -5201,6 +5377,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
5201
5377
|
message = command;
|
|
5202
5378
|
command = 'send';
|
|
5203
5379
|
}
|
|
5380
|
+
// Legacy contract: bad arguments throw synchronously on the callback-style API.
|
|
5204
5381
|
Validator.assertString(instanceName, 'instanceName');
|
|
5205
5382
|
Validator.assertString(command, 'command');
|
|
5206
5383
|
if (!tools.isObject(callback)) {
|
|
@@ -5209,13 +5386,42 @@ export class AdapterClass extends EventEmitter {
|
|
|
5209
5386
|
if (options !== undefined) {
|
|
5210
5387
|
Validator.assertObject(options, 'options');
|
|
5211
5388
|
}
|
|
5212
|
-
|
|
5213
|
-
instanceName,
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5389
|
+
if (tools.isObject(callback)) {
|
|
5390
|
+
this.#fireAndForget(this.#async.sendTo(instanceName, command, message, this.#withSendFlags(options, { callback: callback })), 'Error in sendTo');
|
|
5391
|
+
return;
|
|
5392
|
+
}
|
|
5393
|
+
const cb = callback;
|
|
5394
|
+
if (typeof cb === 'function') {
|
|
5395
|
+
this.sendToAsync(instanceName, command, message, options).then((reply) => cb(reply), (err) => cb(err));
|
|
5396
|
+
return;
|
|
5397
|
+
}
|
|
5398
|
+
this.#fireAndForget(this.#async.sendTo(instanceName, command, message, this.#withSendFlags(options, { expectReply: false })), 'Error in sendTo');
|
|
5399
|
+
}
|
|
5400
|
+
/**
|
|
5401
|
+
* Merges send-control flags into the caller-supplied options for delegation to the async facade.
|
|
5402
|
+
* A non-object `options` is forwarded unchanged so the facade's validation surfaces it.
|
|
5403
|
+
*
|
|
5404
|
+
* @param options caller-supplied send options (unvalidated)
|
|
5405
|
+
* @param flags control flags to apply
|
|
5406
|
+
* @param flags.expectReply when false, the facade sends fire-and-forget
|
|
5407
|
+
* @param flags.callback legacy callback-info header to attach to the message
|
|
5408
|
+
*/
|
|
5409
|
+
#withSendFlags(options, flags) {
|
|
5410
|
+
if (options !== undefined && !tools.isObject(options)) {
|
|
5411
|
+
return options;
|
|
5412
|
+
}
|
|
5413
|
+
return { ...options, ...flags };
|
|
5414
|
+
}
|
|
5415
|
+
/**
|
|
5416
|
+
* Observes a fire-and-forget promise from the callback-compat layer, logging any rejection without
|
|
5417
|
+
* rethrowing. Used only where the legacy callback API had no channel to surface the error; async
|
|
5418
|
+
* callers on {@link AsyncAdapter} instead await and catch.
|
|
5419
|
+
*
|
|
5420
|
+
* @param promise the promise to observe
|
|
5421
|
+
* @param errorMessage log prefix describing the failed action
|
|
5422
|
+
*/
|
|
5423
|
+
#fireAndForget(promise, errorMessage) {
|
|
5424
|
+
promise.catch((e) => this._logger.error(`${this.namespaceLog} ${errorMessage}: ${e instanceof Error ? e.message : String(e)}`));
|
|
5219
5425
|
}
|
|
5220
5426
|
/**
|
|
5221
5427
|
* Async version of sendTo
|
|
@@ -5227,124 +5433,16 @@ export class AdapterClass extends EventEmitter {
|
|
|
5227
5433
|
* @param options optional options to define a timeout. This allows to get an error callback if no answer received in time (only if target is specific instance)
|
|
5228
5434
|
*/
|
|
5229
5435
|
sendToAsync(instanceName, command, message, options) {
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
}
|
|
5235
|
-
resolve(resOrError);
|
|
5236
|
-
};
|
|
5237
|
-
// validation takes place inside sendTo so skip here
|
|
5238
|
-
this.sendTo(instanceName, command, message, callback, options);
|
|
5239
|
-
});
|
|
5240
|
-
}
|
|
5241
|
-
async _sendTo(_options) {
|
|
5242
|
-
const { command, message, callback, options } = _options;
|
|
5243
|
-
let { instanceName } = _options;
|
|
5244
|
-
const obj = {
|
|
5245
|
-
command,
|
|
5246
|
-
message,
|
|
5247
|
-
from: `system.adapter.${this.namespace}`,
|
|
5248
|
-
};
|
|
5249
|
-
if (!instanceName) {
|
|
5250
|
-
// @ts-expect-error TODO it could also be the cb object
|
|
5251
|
-
return tools.maybeCallbackWithError(callback, 'No instanceName provided or not a string');
|
|
5252
|
-
}
|
|
5253
|
-
if (!instanceName.startsWith('system.adapter.')) {
|
|
5254
|
-
instanceName = `system.adapter.${instanceName}`;
|
|
5255
|
-
}
|
|
5256
|
-
if (!this.#states) {
|
|
5257
|
-
// if states is no longer existing, we do not need to unsubscribe
|
|
5258
|
-
this._logger.info(`${this.namespaceLog} sendTo not processed because States database not connected`);
|
|
5259
|
-
// @ts-expect-error TODO it could also be the cb object
|
|
5260
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
5261
|
-
}
|
|
5262
|
-
if (typeof message !== 'object') {
|
|
5263
|
-
this._logger.silly(`${this.namespaceLog} sendTo "${command}" to ${instanceName} from system.adapter.${this.namespace}: ${message}`);
|
|
5264
|
-
}
|
|
5265
|
-
else {
|
|
5266
|
-
this._logger.silly(`${this.namespaceLog} sendTo "${command}" to ${instanceName} from system.adapter.${this.namespace}`);
|
|
5267
|
-
}
|
|
5268
|
-
// If not specific instance
|
|
5269
|
-
if (!instanceName.match(/\.[0-9]+$/)) {
|
|
5270
|
-
if (!this.#objects) {
|
|
5271
|
-
this._logger.info(`${this.namespaceLog} sendTo not processed because Objects database not connected`);
|
|
5272
|
-
// @ts-expect-error TODO it could also be the cb object
|
|
5273
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
5274
|
-
}
|
|
5275
|
-
try {
|
|
5276
|
-
// Send it to all instances of adapter
|
|
5277
|
-
const res = await this.#objects.getObjectView('system', 'instance', {
|
|
5278
|
-
startkey: `${instanceName}.`,
|
|
5279
|
-
endkey: `${instanceName}.\u9999`,
|
|
5280
|
-
});
|
|
5281
|
-
if (res) {
|
|
5282
|
-
for (const row of res.rows) {
|
|
5283
|
-
try {
|
|
5284
|
-
await this.#states.pushMessage(row.id, obj);
|
|
5285
|
-
}
|
|
5286
|
-
catch (e) {
|
|
5287
|
-
// @ts-expect-error TODO it could also be the cb object
|
|
5288
|
-
return tools.maybeCallbackWithError(callback, e);
|
|
5289
|
-
}
|
|
5290
|
-
}
|
|
5291
|
-
}
|
|
5292
|
-
}
|
|
5293
|
-
catch {
|
|
5294
|
-
//ignore
|
|
5295
|
-
}
|
|
5436
|
+
if (typeof message === 'function') {
|
|
5437
|
+
options = undefined;
|
|
5438
|
+
message = command;
|
|
5439
|
+
command = 'send';
|
|
5296
5440
|
}
|
|
5297
|
-
else {
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
// force subscribe even no messagebox enabled
|
|
5301
|
-
if (!isMessageboxSupported(this.common) && !this.mboxSubscribed) {
|
|
5302
|
-
this.mboxSubscribed = true;
|
|
5303
|
-
this.#states.subscribeMessage(`system.adapter.${this.namespace}`);
|
|
5304
|
-
}
|
|
5305
|
-
obj.callback = {
|
|
5306
|
-
message,
|
|
5307
|
-
id: this._callbackId++,
|
|
5308
|
-
ack: false,
|
|
5309
|
-
time: Date.now(),
|
|
5310
|
-
};
|
|
5311
|
-
if (this._callbackId >= 0xffffffff) {
|
|
5312
|
-
this._callbackId = 1;
|
|
5313
|
-
}
|
|
5314
|
-
const callbackId = obj.callback.id;
|
|
5315
|
-
let timer;
|
|
5316
|
-
if (options?.timeout) {
|
|
5317
|
-
timer = setTimeout(() => {
|
|
5318
|
-
const callbackObj = this.messageCallbacks.get(callbackId);
|
|
5319
|
-
if (callbackObj) {
|
|
5320
|
-
callbackObj.cb(new Error('Timeout exceeded'));
|
|
5321
|
-
this.messageCallbacks.delete(callbackId);
|
|
5322
|
-
}
|
|
5323
|
-
}, options.timeout);
|
|
5324
|
-
}
|
|
5325
|
-
this.messageCallbacks.set(callbackId, { cb: callback, time: Date.now(), timer });
|
|
5326
|
-
// delete too old callbacks IDs
|
|
5327
|
-
const now = Date.now();
|
|
5328
|
-
for (const [_id, cb] of this.messageCallbacks) {
|
|
5329
|
-
if (now - cb.time > 3_600_000) {
|
|
5330
|
-
this.messageCallbacks.delete(_id);
|
|
5331
|
-
}
|
|
5332
|
-
}
|
|
5333
|
-
}
|
|
5334
|
-
else {
|
|
5335
|
-
// callback is an object
|
|
5336
|
-
obj.callback = callback;
|
|
5337
|
-
obj.callback.ack = true;
|
|
5338
|
-
}
|
|
5339
|
-
}
|
|
5340
|
-
try {
|
|
5341
|
-
await this.#states.pushMessage(instanceName, obj);
|
|
5342
|
-
}
|
|
5343
|
-
catch (e) {
|
|
5344
|
-
// @ts-expect-error TODO it could also be the cb object
|
|
5345
|
-
return tools.maybeCallbackWithError(callback, e);
|
|
5346
|
-
}
|
|
5441
|
+
else if (typeof message === 'undefined') {
|
|
5442
|
+
message = command;
|
|
5443
|
+
command = 'send';
|
|
5347
5444
|
}
|
|
5445
|
+
return this.#async.sendTo(instanceName, command, message, this.#withSendFlags(options, { expectReply: true }));
|
|
5348
5446
|
}
|
|
5349
5447
|
/**
|
|
5350
5448
|
* Send message to specific host or to all hosts.
|
|
@@ -5364,10 +5462,15 @@ export class AdapterClass extends EventEmitter {
|
|
|
5364
5462
|
* ```
|
|
5365
5463
|
*/
|
|
5366
5464
|
sendToHost(hostName, command, message, callback) {
|
|
5465
|
+
if (typeof message === 'function' && typeof callback === 'undefined') {
|
|
5466
|
+
callback = message;
|
|
5467
|
+
message = undefined;
|
|
5468
|
+
}
|
|
5367
5469
|
if (typeof message === 'undefined') {
|
|
5368
5470
|
message = command;
|
|
5369
5471
|
command = 'send';
|
|
5370
5472
|
}
|
|
5473
|
+
// Legacy contract: bad arguments throw synchronously on the callback-style API.
|
|
5371
5474
|
if (hostName !== null) {
|
|
5372
5475
|
Validator.assertString(hostName, 'hostName');
|
|
5373
5476
|
}
|
|
@@ -5375,91 +5478,34 @@ export class AdapterClass extends EventEmitter {
|
|
|
5375
5478
|
if (!tools.isObject(callback)) {
|
|
5376
5479
|
Validator.assertOptionalCallback(callback, 'callback');
|
|
5377
5480
|
}
|
|
5378
|
-
|
|
5379
|
-
hostName,
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
});
|
|
5384
|
-
}
|
|
5385
|
-
async _sendToHost(_options) {
|
|
5386
|
-
const { command, message, callback } = _options;
|
|
5387
|
-
let { hostName } = _options;
|
|
5388
|
-
const obj = { command, message, from: `system.adapter.${this.namespace}` };
|
|
5389
|
-
if (!this.#states) {
|
|
5390
|
-
// if states is no longer existing, we do not need to unsubscribe
|
|
5391
|
-
this._logger.info(`${this.namespaceLog} sendToHost not processed because States database not connected`);
|
|
5392
|
-
// @ts-expect-error TODO it could also be the cb object
|
|
5393
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
5394
|
-
}
|
|
5395
|
-
if (hostName && !hostName.startsWith('system.host.')) {
|
|
5396
|
-
hostName = `system.host.${hostName}`;
|
|
5481
|
+
if (tools.isObject(callback)) {
|
|
5482
|
+
this.#fireAndForget(this.#async.sendToHost(hostName, command, message, {
|
|
5483
|
+
callback: callback,
|
|
5484
|
+
}), 'Error in sendToHost');
|
|
5485
|
+
return;
|
|
5397
5486
|
}
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
}
|
|
5404
|
-
// Send to all hosts
|
|
5405
|
-
this.#objects.getObjectList({
|
|
5406
|
-
startkey: 'system.host.',
|
|
5407
|
-
endkey: `system.host.\u9999`,
|
|
5408
|
-
}, null, async (err, res) => {
|
|
5409
|
-
if (!this.#states) {
|
|
5410
|
-
// if states is no longer existing, we do not need to unsubscribe
|
|
5411
|
-
return;
|
|
5412
|
-
}
|
|
5413
|
-
if (!err && res?.rows.length) {
|
|
5414
|
-
for (const row of res.rows) {
|
|
5415
|
-
const parts = row.id.split('.');
|
|
5416
|
-
// ignore system.host.name.alive and so on
|
|
5417
|
-
if (parts.length === 3) {
|
|
5418
|
-
try {
|
|
5419
|
-
await this.#states.pushMessage(row.id, obj);
|
|
5420
|
-
}
|
|
5421
|
-
catch (e) {
|
|
5422
|
-
// @ts-expect-error TODO it could also be the cb object
|
|
5423
|
-
return tools.maybeCallbackWithError(callback, e);
|
|
5424
|
-
}
|
|
5425
|
-
}
|
|
5426
|
-
}
|
|
5427
|
-
}
|
|
5428
|
-
});
|
|
5487
|
+
const cb = callback;
|
|
5488
|
+
// A broadcast (hostName === null) yields many replies, so the callback is ignored — matching legacy behavior.
|
|
5489
|
+
if (typeof cb === 'function' && hostName !== null) {
|
|
5490
|
+
this.sendToHostAsync(hostName, command, message).then((reply) => cb(reply), (err) => cb(err));
|
|
5491
|
+
return;
|
|
5429
5492
|
}
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
};
|
|
5444
|
-
if (this._callbackId >= 0xffffffff) {
|
|
5445
|
-
this._callbackId = 1;
|
|
5446
|
-
}
|
|
5447
|
-
this.messageCallbacks.set(obj.callback.id, { cb: callback, time: Date.now() });
|
|
5448
|
-
}
|
|
5449
|
-
else {
|
|
5450
|
-
// callback is an object
|
|
5451
|
-
obj.callback = callback;
|
|
5452
|
-
obj.callback.ack = true;
|
|
5453
|
-
}
|
|
5454
|
-
}
|
|
5455
|
-
try {
|
|
5456
|
-
await this.#states.pushMessage(hostName, obj);
|
|
5457
|
-
}
|
|
5458
|
-
catch (e) {
|
|
5459
|
-
// @ts-expect-error TODO it could also be the cb object
|
|
5460
|
-
return tools.maybeCallbackWithError(callback, e);
|
|
5461
|
-
}
|
|
5493
|
+
this.#fireAndForget(this.#async.sendToHost(hostName, command, message, { expectReply: false }), 'Error in sendToHost');
|
|
5494
|
+
}
|
|
5495
|
+
/**
|
|
5496
|
+
* Async version of sendToHost
|
|
5497
|
+
*
|
|
5498
|
+
* @param hostName name of the host where the message must be sent to. E.g. "myPC" or "system.host.myPC". If argument is null, the message will be sent to all hosts.
|
|
5499
|
+
* @param command command name. One of: "cmdExec", "getRepository", "getInstalled", "getVersion", "getDiagData", "getLocationOnDisk", "getDevList", "getLogs", "delLogs", "readDirAsZip", "writeDirAsZip", "readObjectsAsZip", "writeObjectsAsZip", "checkLogging". Commands can be checked in controller.js (function processMessage)
|
|
5500
|
+
* @param message object that will be given as argument for request
|
|
5501
|
+
*/
|
|
5502
|
+
sendToHostAsync(hostName, command, message) {
|
|
5503
|
+
if (typeof message === 'undefined') {
|
|
5504
|
+
message = command;
|
|
5505
|
+
command = 'send';
|
|
5462
5506
|
}
|
|
5507
|
+
// broadcast (null host) yields many responses → never wait for a reply
|
|
5508
|
+
return this.#async.sendToHost(hostName, command, message, { expectReply: hostName !== null });
|
|
5463
5509
|
}
|
|
5464
5510
|
/**
|
|
5465
5511
|
* Send a message to an active UI Client
|
|
@@ -5467,22 +5513,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
5467
5513
|
* @param options clientId and data options
|
|
5468
5514
|
*/
|
|
5469
5515
|
sendToUI(options) {
|
|
5470
|
-
|
|
5471
|
-
throw new Error(tools.ERRORS.ERROR_DB_CLOSED);
|
|
5472
|
-
}
|
|
5473
|
-
const { clientId, data } = options;
|
|
5474
|
-
if (clientId === undefined) {
|
|
5475
|
-
return this.uiMessagingController.sendToAllClients({
|
|
5476
|
-
data,
|
|
5477
|
-
states: this.#states,
|
|
5478
|
-
});
|
|
5479
|
-
}
|
|
5480
|
-
Validator.assertString(clientId, 'clientId');
|
|
5481
|
-
return this.uiMessagingController.sendToClient({
|
|
5482
|
-
clientId,
|
|
5483
|
-
data,
|
|
5484
|
-
states: this.#states,
|
|
5485
|
-
});
|
|
5516
|
+
return this.#async.sendToUI(options);
|
|
5486
5517
|
}
|
|
5487
5518
|
/**
|
|
5488
5519
|
* Send notification with given scope and category to host of this adapter
|
|
@@ -5493,31 +5524,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
5493
5524
|
* @param options - Additional options for the notification, currently `contextData` is supported
|
|
5494
5525
|
*/
|
|
5495
5526
|
async registerNotification(scope, category, message, options) {
|
|
5496
|
-
|
|
5497
|
-
// if states is no longer existing, we do not need to set
|
|
5498
|
-
this._logger.info(`${this.namespaceLog} registerNotification not processed because States database not connected`);
|
|
5499
|
-
throw new Error(tools.ERRORS.ERROR_DB_CLOSED);
|
|
5500
|
-
}
|
|
5501
|
-
Validator.assertString(scope, 'scope');
|
|
5502
|
-
if (category !== null) {
|
|
5503
|
-
Validator.assertString(category, 'category');
|
|
5504
|
-
}
|
|
5505
|
-
Validator.assertString(message, 'message');
|
|
5506
|
-
if (options !== undefined) {
|
|
5507
|
-
Validator.assertObject(options, 'options');
|
|
5508
|
-
}
|
|
5509
|
-
const obj = {
|
|
5510
|
-
command: 'addNotification',
|
|
5511
|
-
message: {
|
|
5512
|
-
scope,
|
|
5513
|
-
category,
|
|
5514
|
-
message,
|
|
5515
|
-
instance: this.namespace,
|
|
5516
|
-
contextData: options?.contextData,
|
|
5517
|
-
},
|
|
5518
|
-
from: `system.adapter.${this.namespace}`,
|
|
5519
|
-
};
|
|
5520
|
-
await this.#states.pushMessage(`system.host.${this.host}`, obj);
|
|
5527
|
+
return this.#async.registerNotification(scope, category, message, options);
|
|
5521
5528
|
}
|
|
5522
5529
|
/**
|
|
5523
5530
|
* Writes value into states DB.
|
|
@@ -5736,7 +5743,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
5736
5743
|
// ignore
|
|
5737
5744
|
}
|
|
5738
5745
|
if (!userAcl) {
|
|
5739
|
-
// User does not
|
|
5746
|
+
// User does not exist
|
|
5740
5747
|
this._logger.error(`${this.namespaceLog} unknown user "${options.user}"`);
|
|
5741
5748
|
return options;
|
|
5742
5749
|
}
|
|
@@ -5758,8 +5765,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
5758
5765
|
// read all groups for this user
|
|
5759
5766
|
this.users[options.user] = {
|
|
5760
5767
|
groups: options.groups,
|
|
5761
|
-
|
|
5762
|
-
acl: userAcl.common?.acl || {},
|
|
5768
|
+
acl: {},
|
|
5763
5769
|
};
|
|
5764
5770
|
await this._getGroups(options.groups);
|
|
5765
5771
|
// combine all rights
|
|
@@ -5770,9 +5776,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
5770
5776
|
}
|
|
5771
5777
|
const group = this.groups[gName];
|
|
5772
5778
|
if (group.common?.acl?.file) {
|
|
5773
|
-
if (!user.acl
|
|
5774
|
-
user.acl
|
|
5775
|
-
user.acl.file
|
|
5779
|
+
if (!user.acl?.file) {
|
|
5780
|
+
user.acl ||= {};
|
|
5781
|
+
user.acl.file ||= {};
|
|
5776
5782
|
user.acl.file.create = group.common.acl.file.create;
|
|
5777
5783
|
user.acl.file.read = group.common.acl.file.read;
|
|
5778
5784
|
user.acl.file.write = group.common.acl.file.write;
|
|
@@ -5780,17 +5786,17 @@ export class AdapterClass extends EventEmitter {
|
|
|
5780
5786
|
user.acl.file.list = group.common.acl.file.list;
|
|
5781
5787
|
}
|
|
5782
5788
|
else {
|
|
5783
|
-
user.acl.file.create
|
|
5784
|
-
user.acl.file.read
|
|
5785
|
-
user.acl.file.write
|
|
5786
|
-
user.acl.file.delete
|
|
5787
|
-
user.acl.file.list
|
|
5789
|
+
user.acl.file.create ||= group.common.acl.file.create;
|
|
5790
|
+
user.acl.file.read ||= group.common.acl.file.read;
|
|
5791
|
+
user.acl.file.write ||= group.common.acl.file.write;
|
|
5792
|
+
user.acl.file.delete ||= group.common.acl.file.delete;
|
|
5793
|
+
user.acl.file.list ||= group.common.acl.file.list;
|
|
5788
5794
|
}
|
|
5789
5795
|
}
|
|
5790
5796
|
if (group.common?.acl?.object) {
|
|
5791
5797
|
if (!user.acl || !user.acl.object) {
|
|
5792
|
-
user.acl
|
|
5793
|
-
user.acl.object
|
|
5798
|
+
user.acl ||= {};
|
|
5799
|
+
user.acl.object ||= {};
|
|
5794
5800
|
user.acl.object.create = group.common.acl.object.create;
|
|
5795
5801
|
user.acl.object.read = group.common.acl.object.read;
|
|
5796
5802
|
user.acl.object.write = group.common.acl.object.write;
|
|
@@ -5798,17 +5804,17 @@ export class AdapterClass extends EventEmitter {
|
|
|
5798
5804
|
user.acl.object.list = group.common.acl.object.list;
|
|
5799
5805
|
}
|
|
5800
5806
|
else {
|
|
5801
|
-
user.acl.object.create
|
|
5802
|
-
user.acl.object.read
|
|
5803
|
-
user.acl.object.write
|
|
5804
|
-
user.acl.object.delete
|
|
5805
|
-
user.acl.object.list
|
|
5807
|
+
user.acl.object.create ||= group.common.acl.object.create;
|
|
5808
|
+
user.acl.object.read ||= group.common.acl.object.read;
|
|
5809
|
+
user.acl.object.write ||= group.common.acl.object.write;
|
|
5810
|
+
user.acl.object.delete ||= group.common.acl.object.delete;
|
|
5811
|
+
user.acl.object.list ||= group.common.acl.object.list;
|
|
5806
5812
|
}
|
|
5807
5813
|
}
|
|
5808
5814
|
if (group.common?.acl?.users) {
|
|
5809
5815
|
if (!user.acl || !user.acl.users) {
|
|
5810
|
-
user.acl
|
|
5811
|
-
user.acl.users
|
|
5816
|
+
user.acl ||= {};
|
|
5817
|
+
user.acl.users ||= {};
|
|
5812
5818
|
user.acl.users.create = group.common.acl.users.create;
|
|
5813
5819
|
user.acl.users.read = group.common.acl.users.read;
|
|
5814
5820
|
user.acl.users.write = group.common.acl.users.write;
|
|
@@ -5816,17 +5822,17 @@ export class AdapterClass extends EventEmitter {
|
|
|
5816
5822
|
user.acl.users.list = group.common.acl.users.list;
|
|
5817
5823
|
}
|
|
5818
5824
|
else {
|
|
5819
|
-
user.acl.users.create
|
|
5820
|
-
user.acl.users.read
|
|
5821
|
-
user.acl.users.write
|
|
5822
|
-
user.acl.users.delete
|
|
5823
|
-
user.acl.users.list
|
|
5825
|
+
user.acl.users.create ||= group.common.acl.users.create;
|
|
5826
|
+
user.acl.users.read ||= group.common.acl.users.read;
|
|
5827
|
+
user.acl.users.write ||= group.common.acl.users.write;
|
|
5828
|
+
user.acl.users.delete ||= group.common.acl.users.delete;
|
|
5829
|
+
user.acl.users.list ||= group.common.acl.users.list;
|
|
5824
5830
|
}
|
|
5825
5831
|
}
|
|
5826
5832
|
if (group.common?.acl?.state) {
|
|
5827
5833
|
if (!user.acl || !user.acl.state) {
|
|
5828
|
-
user.acl
|
|
5829
|
-
user.acl.state
|
|
5834
|
+
user.acl ||= {};
|
|
5835
|
+
user.acl.state ||= {};
|
|
5830
5836
|
user.acl.state.create = group.common.acl.state.create;
|
|
5831
5837
|
user.acl.state.read = group.common.acl.state.read;
|
|
5832
5838
|
user.acl.state.write = group.common.acl.state.write;
|
|
@@ -5834,11 +5840,11 @@ export class AdapterClass extends EventEmitter {
|
|
|
5834
5840
|
user.acl.state.list = group.common.acl.state.list;
|
|
5835
5841
|
}
|
|
5836
5842
|
else {
|
|
5837
|
-
user.acl.state.create
|
|
5838
|
-
user.acl.state.read
|
|
5839
|
-
user.acl.state.write
|
|
5840
|
-
user.acl.state.delete
|
|
5841
|
-
user.acl.state.list
|
|
5843
|
+
user.acl.state.create ||= group.common.acl.state.create;
|
|
5844
|
+
user.acl.state.read ||= group.common.acl.state.read;
|
|
5845
|
+
user.acl.state.write ||= group.common.acl.state.write;
|
|
5846
|
+
user.acl.state.delete ||= group.common.acl.state.delete;
|
|
5847
|
+
user.acl.state.list ||= group.common.acl.state.list;
|
|
5842
5848
|
}
|
|
5843
5849
|
}
|
|
5844
5850
|
}
|
|
@@ -5846,10 +5852,18 @@ export class AdapterClass extends EventEmitter {
|
|
|
5846
5852
|
return options;
|
|
5847
5853
|
}
|
|
5848
5854
|
_checkState(obj, options, command) {
|
|
5855
|
+
// Members of the administrator group are not restricted by the ACL of a state, the same as for objects and files
|
|
5856
|
+
if (options.groups?.includes(SYSTEM_ADMIN_GROUP)) {
|
|
5857
|
+
return true;
|
|
5858
|
+
}
|
|
5849
5859
|
const limitToOwnerRights = options.limitToOwnerRights === true;
|
|
5850
5860
|
if (obj?.acl) {
|
|
5851
|
-
obj.acl.state
|
|
5861
|
+
obj.acl.state ||= obj.acl.object;
|
|
5852
5862
|
if (obj.acl.state) {
|
|
5863
|
+
if (!options.acl) {
|
|
5864
|
+
this._logger.warn(`${this.namespaceLog} Permission error for user "${options.user} on "${obj._id}": ${command}`);
|
|
5865
|
+
return false;
|
|
5866
|
+
}
|
|
5853
5867
|
// If user is owner
|
|
5854
5868
|
if (options.user === obj.acl.owner) {
|
|
5855
5869
|
if (command === 'setState' || command === 'delState') {
|
|
@@ -5876,7 +5890,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
5876
5890
|
this._logger.warn(`${this.namespaceLog} Called unknown command on "${obj._id}": ${command}`);
|
|
5877
5891
|
}
|
|
5878
5892
|
}
|
|
5879
|
-
else if (options.groups
|
|
5893
|
+
else if (options.groups?.includes(obj.acl.ownerGroup) && !limitToOwnerRights) {
|
|
5880
5894
|
if (command === 'setState' || command === 'delState') {
|
|
5881
5895
|
if (command === 'delState' && !options.acl.state.delete) {
|
|
5882
5896
|
this._logger.warn(`${this.namespaceLog} Permission error for user "${options.user} on "${obj._id}": ${command}`);
|
|
@@ -6166,7 +6180,6 @@ export class AdapterClass extends EventEmitter {
|
|
|
6166
6180
|
return tools.maybeCallbackWithError(callback, null, res.id, res.notChanged);
|
|
6167
6181
|
}
|
|
6168
6182
|
const res = await this._setStateChangedHelper(fixedId, stateObj);
|
|
6169
|
-
// @ts-expect-error todo fix it
|
|
6170
6183
|
return tools.maybeCallbackWithError(callback, null, res.id, res.notChanged);
|
|
6171
6184
|
}
|
|
6172
6185
|
/**
|
|
@@ -6252,7 +6265,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
6252
6265
|
}
|
|
6253
6266
|
// if state.from provided, we use it else, we set default property
|
|
6254
6267
|
state.from =
|
|
6255
|
-
typeof state.from === 'string' && state.from !== ''
|
|
6268
|
+
typeof state.from === 'string' && state.from !== ''
|
|
6269
|
+
? state.from
|
|
6270
|
+
: `system.adapter.${this.namespace}`;
|
|
6256
6271
|
state.user = options?.user || SYSTEM_ADMIN_USER;
|
|
6257
6272
|
if (!id || typeof id !== 'string') {
|
|
6258
6273
|
const warn = id ? `ID can be only string and not "${typeof id}"` : `Empty ID: ${JSON.stringify(state)}`;
|
|
@@ -6260,10 +6275,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
6260
6275
|
return tools.maybeCallbackWithError(callback, warn);
|
|
6261
6276
|
}
|
|
6262
6277
|
id = this.fixForbiddenCharsInId(id);
|
|
6263
|
-
if (options?.user &&
|
|
6278
|
+
if (options?.user &&
|
|
6279
|
+
options.user !== SYSTEM_ADMIN_USER) {
|
|
6264
6280
|
let obj;
|
|
6265
6281
|
try {
|
|
6266
|
-
obj = (await this._checkStates(id, options, 'setState'))
|
|
6282
|
+
obj = (await this._checkStates(id, options, 'setState'))
|
|
6283
|
+
.objs[0];
|
|
6267
6284
|
}
|
|
6268
6285
|
catch (e) {
|
|
6269
6286
|
return tools.maybeCallbackWithError(callback, e);
|
|
@@ -6311,8 +6328,8 @@ export class AdapterClass extends EventEmitter {
|
|
|
6311
6328
|
this.outputCount++;
|
|
6312
6329
|
this.#states.setState(aliasId, tools.formatAliasValue({
|
|
6313
6330
|
sourceCommon: obj?.common,
|
|
6314
|
-
targetCommon: targetObj
|
|
6315
|
-
state,
|
|
6331
|
+
targetCommon: targetObj?.common,
|
|
6332
|
+
state: state,
|
|
6316
6333
|
logger: this._logger,
|
|
6317
6334
|
logNamespace: this.namespaceLog,
|
|
6318
6335
|
sourceId: obj?._id,
|
|
@@ -6375,7 +6392,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
6375
6392
|
this.#states.setState(targetId, tools.formatAliasValue({
|
|
6376
6393
|
sourceCommon: obj.common,
|
|
6377
6394
|
targetCommon: targetObj?.common,
|
|
6378
|
-
state,
|
|
6395
|
+
state: state,
|
|
6379
6396
|
logger: this._logger,
|
|
6380
6397
|
logNamespace: this.namespaceLog,
|
|
6381
6398
|
sourceId: obj._id,
|
|
@@ -6475,7 +6492,8 @@ export class AdapterClass extends EventEmitter {
|
|
|
6475
6492
|
// wrap non-object values in a state object
|
|
6476
6493
|
state = state !== undefined ? { val: state } : {};
|
|
6477
6494
|
}
|
|
6478
|
-
if (state.val === undefined &&
|
|
6495
|
+
if (state.val === undefined &&
|
|
6496
|
+
!Object.keys(state).length) {
|
|
6479
6497
|
// undefined is not allowed as state.val -> return
|
|
6480
6498
|
this._logger.info(`${this.namespaceLog} undefined is not a valid state value for id "${id}"`);
|
|
6481
6499
|
// TODO: reactivate line below + test in in next controller version (02.05.2021)
|
|
@@ -6486,10 +6504,14 @@ export class AdapterClass extends EventEmitter {
|
|
|
6486
6504
|
}
|
|
6487
6505
|
// if state.from provided, we use it else, we set default property
|
|
6488
6506
|
state.from =
|
|
6489
|
-
typeof state.from === 'string' && state.from !== ''
|
|
6490
|
-
|
|
6507
|
+
typeof state.from === 'string' && state.from !== ''
|
|
6508
|
+
? state.from
|
|
6509
|
+
: `system.adapter.${this.namespace}`;
|
|
6510
|
+
state.user =
|
|
6511
|
+
options?.user || SYSTEM_ADMIN_USER;
|
|
6491
6512
|
id = this.fixForbiddenCharsInId(id);
|
|
6492
|
-
if (options?.user &&
|
|
6513
|
+
if (options?.user &&
|
|
6514
|
+
options.user !== SYSTEM_ADMIN_USER) {
|
|
6493
6515
|
try {
|
|
6494
6516
|
await this._checkStates(id, options, 'setState');
|
|
6495
6517
|
}
|
|
@@ -6517,7 +6539,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
6517
6539
|
* }
|
|
6518
6540
|
* ```
|
|
6519
6541
|
*
|
|
6520
|
-
* See possible attributes of the state in
|
|
6542
|
+
* See possible attributes of the state in `setState` explanation
|
|
6521
6543
|
*/
|
|
6522
6544
|
getState(id, options, callback) {
|
|
6523
6545
|
// we use any types here, because validation takes place in foreign method
|
|
@@ -6542,7 +6564,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
6542
6564
|
* }
|
|
6543
6565
|
* ```
|
|
6544
6566
|
*
|
|
6545
|
-
* See possible attributes of the state in
|
|
6567
|
+
* See possible attributes of the state in `setState` explanation
|
|
6546
6568
|
*/
|
|
6547
6569
|
getForeignState(id, options, callback) {
|
|
6548
6570
|
if (typeof options === 'function') {
|
|
@@ -6646,7 +6668,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
6646
6668
|
if (this.oStates && this.oStates[id]) {
|
|
6647
6669
|
return tools.maybeCallbackWithError(callback, null, this.oStates[id]);
|
|
6648
6670
|
}
|
|
6649
|
-
return this.#states.getState(id, callback);
|
|
6671
|
+
return callback ? this.#states.getState(id, callback) : this.#states.getState(id);
|
|
6650
6672
|
}
|
|
6651
6673
|
}
|
|
6652
6674
|
// find out default history instance
|
|
@@ -6731,7 +6753,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
6731
6753
|
* }
|
|
6732
6754
|
* ```
|
|
6733
6755
|
*
|
|
6734
|
-
* See possible attributes of the state in
|
|
6756
|
+
* See possible attributes of the state in `setState` explanation
|
|
6735
6757
|
*/
|
|
6736
6758
|
getHistory(id, options, callback) {
|
|
6737
6759
|
if (typeof options === 'function') {
|
|
@@ -6753,13 +6775,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
6753
6775
|
this._utils.validateId(id, true, null);
|
|
6754
6776
|
}
|
|
6755
6777
|
catch (e) {
|
|
6756
|
-
// @ts-expect-error
|
|
6757
6778
|
return tools.maybeCallbackWithError(callback, e);
|
|
6758
6779
|
}
|
|
6759
|
-
options
|
|
6760
|
-
options.end
|
|
6780
|
+
options ||= {};
|
|
6781
|
+
options.end ||= Date.now() + 5000000;
|
|
6761
6782
|
if (!options.count && !options.start) {
|
|
6762
|
-
options.start
|
|
6783
|
+
options.start ||= Date.now() - 604800000; // - 1 week
|
|
6763
6784
|
}
|
|
6764
6785
|
if (!options.instance) {
|
|
6765
6786
|
if (!this.defaultHistory) {
|
|
@@ -6774,12 +6795,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
6774
6795
|
await this._checkStates(id, options, 'getState');
|
|
6775
6796
|
}
|
|
6776
6797
|
catch (e) {
|
|
6777
|
-
// @ts-expect-error
|
|
6778
6798
|
return tools.maybeCallbackWithError(callback, e);
|
|
6779
6799
|
}
|
|
6780
6800
|
}
|
|
6781
|
-
this.sendTo(options.instance || 'history.0', 'getHistory', { id, options }, res => {
|
|
6782
|
-
// @ts-expect-error
|
|
6801
|
+
this.sendTo(options.instance || 'history.0', 'getHistory', { id, options }, (res) => {
|
|
6783
6802
|
tools.maybeCallbackWithError(callback, res.error, res.result, res.step, res.sessionId);
|
|
6784
6803
|
});
|
|
6785
6804
|
}
|
|
@@ -6806,7 +6825,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
6806
6825
|
}
|
|
6807
6826
|
/**
|
|
6808
6827
|
* Deletes a state of this instance.
|
|
6809
|
-
* The object will NOT be deleted. If you want to delete it too, use
|
|
6828
|
+
* The object will NOT be deleted. If you want to delete it too, use `delObject` instead.
|
|
6810
6829
|
*
|
|
6811
6830
|
* It is not required to provice the adapter namespace, because it will automatically be added.
|
|
6812
6831
|
* E.g. to delete "adapterName.X.myObject", only "myObject" is required as ID.
|
|
@@ -6849,7 +6868,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
6849
6868
|
}
|
|
6850
6869
|
/**
|
|
6851
6870
|
* Deletes a state of any adapter.
|
|
6852
|
-
* The object is NOT deleted. If you want to delete it too, use
|
|
6871
|
+
* The object is NOT deleted. If you want to delete it too, use `delForeignObject` instead.
|
|
6853
6872
|
*
|
|
6854
6873
|
* No error is returned if state does not exist.
|
|
6855
6874
|
*
|
|
@@ -6893,7 +6912,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
6893
6912
|
return tools.maybeCallbackWithError(callback, e);
|
|
6894
6913
|
}
|
|
6895
6914
|
}
|
|
6896
|
-
|
|
6915
|
+
if (callback) {
|
|
6916
|
+
this.#states.delState(id, callback);
|
|
6917
|
+
}
|
|
6918
|
+
else {
|
|
6919
|
+
await this.#states.delState(id);
|
|
6920
|
+
}
|
|
6897
6921
|
}
|
|
6898
6922
|
/**
|
|
6899
6923
|
* Read all states of this adapter, that pass the pattern
|
|
@@ -6928,7 +6952,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
6928
6952
|
const arr = await this.#states.getStates(keys);
|
|
6929
6953
|
const result = {};
|
|
6930
6954
|
for (let i = 0; i < keys.length; i++) {
|
|
6931
|
-
const obj = targetObjs
|
|
6955
|
+
const obj = targetObjs?.[i];
|
|
6932
6956
|
if (obj?.common?.alias && srcObjs) {
|
|
6933
6957
|
const srcObj = srcObjs[i];
|
|
6934
6958
|
if (srcObj) {
|
|
@@ -7041,12 +7065,12 @@ export class AdapterClass extends EventEmitter {
|
|
|
7041
7065
|
async _getForeignStates(_options) {
|
|
7042
7066
|
const { options, pattern, callback } = _options;
|
|
7043
7067
|
if (!this.#states) {
|
|
7044
|
-
// if states
|
|
7068
|
+
// if states are no longer existing, we do not need to unsubscribe
|
|
7045
7069
|
this._logger.info(`${this.namespaceLog} getForeignStates not processed because States database not connected`);
|
|
7046
7070
|
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
7047
7071
|
}
|
|
7048
7072
|
if (!this.#objects) {
|
|
7049
|
-
// if states
|
|
7073
|
+
// if states are no longer existing, we do not need to unsubscribe
|
|
7050
7074
|
this._logger.info(`${this.namespaceLog} getForeignStates not processed because Objects database not connected`);
|
|
7051
7075
|
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
7052
7076
|
}
|
|
@@ -7063,7 +7087,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
7063
7087
|
}
|
|
7064
7088
|
}
|
|
7065
7089
|
else {
|
|
7066
|
-
const res = await this._processStates(pattern, options
|
|
7090
|
+
const res = await this._processStates(pattern, options._objects);
|
|
7067
7091
|
return tools.maybeCallbackWithError(callback, null, res);
|
|
7068
7092
|
}
|
|
7069
7093
|
}
|
|
@@ -7131,90 +7155,6 @@ export class AdapterClass extends EventEmitter {
|
|
|
7131
7155
|
}
|
|
7132
7156
|
}
|
|
7133
7157
|
}
|
|
7134
|
-
/**
|
|
7135
|
-
* Add subscription for given alias, if it is not a state it will be ignored
|
|
7136
|
-
*
|
|
7137
|
-
* @param aliasObj the alias object
|
|
7138
|
-
* @param pattern pattern to subscribe for
|
|
7139
|
-
*/
|
|
7140
|
-
async _addAliasSubscribe(aliasObj, pattern) {
|
|
7141
|
-
if (aliasObj.type !== 'state') {
|
|
7142
|
-
// no state types do not need to be subscribed
|
|
7143
|
-
return;
|
|
7144
|
-
}
|
|
7145
|
-
if (!aliasObj.common?.alias?.id) {
|
|
7146
|
-
// if state and no id given
|
|
7147
|
-
this._logger.warn(`${this.namespaceLog} Alias ${aliasObj._id} has no target 5`);
|
|
7148
|
-
throw new Error(`Alias ${aliasObj._id} has no target`);
|
|
7149
|
-
}
|
|
7150
|
-
// id can be string or can have attribute read
|
|
7151
|
-
const sourceId = tools.isObject(aliasObj.common.alias.id)
|
|
7152
|
-
? aliasObj.common.alias.id.read
|
|
7153
|
-
: aliasObj.common.alias.id;
|
|
7154
|
-
// validate here because we use objects/states db directly
|
|
7155
|
-
try {
|
|
7156
|
-
this._utils.validateId(sourceId, true, null);
|
|
7157
|
-
}
|
|
7158
|
-
catch (e) {
|
|
7159
|
-
throw new Error(`Error validating alias id of ${aliasObj._id}: ${e.message}`);
|
|
7160
|
-
}
|
|
7161
|
-
const targetEntry = {
|
|
7162
|
-
alias: deepClone(aliasObj.common.alias),
|
|
7163
|
-
id: aliasObj._id,
|
|
7164
|
-
pattern,
|
|
7165
|
-
type: aliasObj.common.type,
|
|
7166
|
-
max: aliasObj.common.max,
|
|
7167
|
-
min: aliasObj.common.min,
|
|
7168
|
-
unit: aliasObj.common.unit,
|
|
7169
|
-
};
|
|
7170
|
-
let aliasDetails;
|
|
7171
|
-
if (!this.aliases.has(sourceId)) {
|
|
7172
|
-
aliasDetails = { targets: [] };
|
|
7173
|
-
// add the alias before doing anything async, so if a delete comes in between we can detect it
|
|
7174
|
-
this.aliases.set(sourceId, aliasDetails);
|
|
7175
|
-
}
|
|
7176
|
-
else {
|
|
7177
|
-
aliasDetails = this.aliases.get(sourceId);
|
|
7178
|
-
}
|
|
7179
|
-
if (!aliasDetails.source) {
|
|
7180
|
-
await this.#states.subscribe(sourceId);
|
|
7181
|
-
// we ignore permissions on the source object and thus get it as admin user
|
|
7182
|
-
const sourceObj = await this.#objects.getObject(sourceId, { user: SYSTEM_ADMIN_USER });
|
|
7183
|
-
// if we have a common and the alias has not been removed in-between
|
|
7184
|
-
if (sourceObj?.common && this.aliases.has(sourceObj._id)) {
|
|
7185
|
-
aliasDetails.source = {
|
|
7186
|
-
min: sourceObj.common.min,
|
|
7187
|
-
max: sourceObj.common.max,
|
|
7188
|
-
type: sourceObj.common.type,
|
|
7189
|
-
unit: sourceObj.common.unit,
|
|
7190
|
-
};
|
|
7191
|
-
}
|
|
7192
|
-
}
|
|
7193
|
-
// add the alias target after we have ensured that we have the source set
|
|
7194
|
-
aliasDetails.targets.push(targetEntry);
|
|
7195
|
-
}
|
|
7196
|
-
/**
|
|
7197
|
-
* Remove an alias subscribe
|
|
7198
|
-
*
|
|
7199
|
-
* @param sourceId id of the source object
|
|
7200
|
-
* @param aliasObjOrIdx the alias target or the index of the targets array
|
|
7201
|
-
*/
|
|
7202
|
-
async _removeAliasSubscribe(sourceId, aliasObjOrIdx) {
|
|
7203
|
-
if (!this.aliases.has(sourceId)) {
|
|
7204
|
-
return;
|
|
7205
|
-
}
|
|
7206
|
-
const alias = this.aliases.get(sourceId);
|
|
7207
|
-
// remove from targets array
|
|
7208
|
-
const pos = typeof aliasObjOrIdx === 'number' ? aliasObjOrIdx : alias.targets.indexOf(aliasObjOrIdx);
|
|
7209
|
-
if (pos !== -1) {
|
|
7210
|
-
alias.targets.splice(pos, 1);
|
|
7211
|
-
// unsubscribe if no more aliases exists
|
|
7212
|
-
if (!alias.targets.length) {
|
|
7213
|
-
this.aliases.delete(sourceId);
|
|
7214
|
-
await this.#states.unsubscribe(sourceId);
|
|
7215
|
-
}
|
|
7216
|
-
}
|
|
7217
|
-
}
|
|
7218
7158
|
/**
|
|
7219
7159
|
* Subscribe for changes on all states of all adapters (and system states), that pass the pattern
|
|
7220
7160
|
*
|
|
@@ -7241,167 +7181,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
7241
7181
|
Validator.assertObject(options, 'options');
|
|
7242
7182
|
}
|
|
7243
7183
|
Validator.assertPattern(pattern, 'pattern');
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
// Todo check rights for options
|
|
7249
|
-
await this._autoSubscribeOn();
|
|
7250
|
-
if (!this.#states) {
|
|
7251
|
-
// if states is no longer existing, we do not need to unsubscribe
|
|
7252
|
-
this._logger.info(`${this.namespaceLog} subscribeForeignStates not processed because States database not connected`);
|
|
7253
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
7254
|
-
}
|
|
7255
|
-
if (!this.#objects) {
|
|
7256
|
-
this._logger.info(`${this.namespaceLog} subscribeForeignStates not processed because Objects database not connected`);
|
|
7257
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
7258
|
-
}
|
|
7259
|
-
// compare if this pattern for one of auto-subscribe adapters
|
|
7260
|
-
for (const autoSubEntry of this.autoSubscribe) {
|
|
7261
|
-
if (typeof pattern === 'string' && (pattern === '*' || pattern.startsWith(`${autoSubEntry}.`))) {
|
|
7262
|
-
// put this pattern into adapter list
|
|
7263
|
-
let state;
|
|
7264
|
-
try {
|
|
7265
|
-
state = await this.#states.getState(`system.adapter.${autoSubEntry}.subscribes`);
|
|
7266
|
-
}
|
|
7267
|
-
catch {
|
|
7268
|
-
// ignore
|
|
7269
|
-
}
|
|
7270
|
-
state = state || { val: '{}' };
|
|
7271
|
-
state.val = state.val || '{}';
|
|
7272
|
-
let subs;
|
|
7273
|
-
try {
|
|
7274
|
-
subs = JSON.parse(state.val);
|
|
7275
|
-
}
|
|
7276
|
-
catch {
|
|
7277
|
-
this._logger.error(`${this.namespaceLog} Cannot parse subscribes for "${autoSubEntry}.subscribes"`);
|
|
7278
|
-
}
|
|
7279
|
-
// validate that correct structure read from state.val
|
|
7280
|
-
if (!tools.isObject(subs)) {
|
|
7281
|
-
subs = {};
|
|
7282
|
-
}
|
|
7283
|
-
if (!tools.isObject(subs[pattern])) {
|
|
7284
|
-
subs[pattern] = {};
|
|
7285
|
-
}
|
|
7286
|
-
if (typeof subs[pattern][this.namespace] !== 'number') {
|
|
7287
|
-
subs[pattern][this.namespace] = 0;
|
|
7288
|
-
}
|
|
7289
|
-
subs[pattern][this.namespace]++;
|
|
7290
|
-
this.outputCount++;
|
|
7291
|
-
await this.#states.setState(`system.adapter.${autoSubEntry}.subscribes`, JSON.stringify(subs));
|
|
7292
|
-
}
|
|
7293
|
-
}
|
|
7294
|
-
if (Array.isArray(pattern)) {
|
|
7295
|
-
// get all aliases
|
|
7296
|
-
const aliasesIds = pattern
|
|
7297
|
-
.map(id => (id.startsWith(ALIAS_STARTS_WITH) ? id : null))
|
|
7298
|
-
.filter(id => id);
|
|
7299
|
-
// get all non aliases
|
|
7300
|
-
const nonAliasesIds = pattern
|
|
7301
|
-
.map(id => (!id.startsWith(ALIAS_STARTS_WITH) ? id : null))
|
|
7302
|
-
.filter(id => id);
|
|
7303
|
-
for (const aliasPattern of pattern) {
|
|
7304
|
-
if ((aliasPattern.startsWith(ALIAS_STARTS_WITH) || aliasPattern.includes('*')) &&
|
|
7305
|
-
!this.aliasPatterns.has(aliasPattern)) {
|
|
7306
|
-
// it's a new alias conform pattern to store
|
|
7307
|
-
this.aliasPatterns.add(aliasPattern);
|
|
7308
|
-
}
|
|
7309
|
-
}
|
|
7310
|
-
const promises = [];
|
|
7311
|
-
if (aliasesIds.length) {
|
|
7312
|
-
if (!this._aliasObjectsSubscribed) {
|
|
7313
|
-
this._aliasObjectsSubscribed = true;
|
|
7314
|
-
this.#objects.subscribe(`${ALIAS_STARTS_WITH}*`);
|
|
7315
|
-
}
|
|
7316
|
-
const aliasObjs = await this._getObjectsByArray(aliasesIds, options);
|
|
7317
|
-
for (const aliasObj of aliasObjs) {
|
|
7318
|
-
if (aliasObj) {
|
|
7319
|
-
promises.push(this._addAliasSubscribe(aliasObj, aliasObj._id));
|
|
7320
|
-
}
|
|
7321
|
-
}
|
|
7322
|
-
}
|
|
7323
|
-
if (nonAliasesIds.length) {
|
|
7324
|
-
for (const id of nonAliasesIds) {
|
|
7325
|
-
promises.push(new Promise(resolve => this.#states.subscribeUser(id, resolve)));
|
|
7326
|
-
}
|
|
7327
|
-
}
|
|
7328
|
-
try {
|
|
7329
|
-
await Promise.all(promises);
|
|
7330
|
-
}
|
|
7331
|
-
catch (e) {
|
|
7332
|
-
this._logger.error(`${this.namespaceLog} Error on "subscribeForeignStates": ${e.message}`);
|
|
7333
|
-
}
|
|
7334
|
-
return tools.maybeCallback(callback);
|
|
7335
|
-
}
|
|
7336
|
-
else if (pattern.includes('*')) {
|
|
7337
|
-
if (pattern === '*' || pattern.startsWith(ALIAS_STARTS_WITH)) {
|
|
7338
|
-
if (!this._aliasObjectsSubscribed) {
|
|
7339
|
-
this._aliasObjectsSubscribed = true;
|
|
7340
|
-
this.#objects.subscribe(`${ALIAS_STARTS_WITH}*`);
|
|
7341
|
-
}
|
|
7342
|
-
// read all aliases
|
|
7343
|
-
try {
|
|
7344
|
-
// @ts-expect-error adjust types
|
|
7345
|
-
const objs = await this.getForeignObjectsAsync(pattern, null, null, options);
|
|
7346
|
-
const promises = [];
|
|
7347
|
-
if (!this.aliasPatterns.has(pattern)) {
|
|
7348
|
-
// it's a new pattern to store
|
|
7349
|
-
this.aliasPatterns.add(pattern);
|
|
7350
|
-
}
|
|
7351
|
-
for (const id of Object.keys(objs)) {
|
|
7352
|
-
// If alias
|
|
7353
|
-
if (id.startsWith(ALIAS_STARTS_WITH)) {
|
|
7354
|
-
const aliasObj = objs[id];
|
|
7355
|
-
promises.push(this._addAliasSubscribe(aliasObj, pattern));
|
|
7356
|
-
}
|
|
7357
|
-
}
|
|
7358
|
-
try {
|
|
7359
|
-
await Promise.all(promises);
|
|
7360
|
-
}
|
|
7361
|
-
catch (e) {
|
|
7362
|
-
this._logger.error(`${this.namespaceLog} Error on "subscribeForeignStates": ${e.message}`);
|
|
7363
|
-
}
|
|
7364
|
-
if (!this.#states) {
|
|
7365
|
-
// if states is no longer existing, we do not need to unsubscribe
|
|
7366
|
-
this._logger.info(`${this.namespaceLog} subscribeForeignStates not processed because States database not connected`);
|
|
7367
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
7368
|
-
}
|
|
7369
|
-
if (promises.length && pattern !== '*') {
|
|
7370
|
-
return tools.maybeCallback(callback);
|
|
7371
|
-
}
|
|
7372
|
-
// no alias objects found or pattern *
|
|
7373
|
-
this.#states.subscribeUser(pattern, callback);
|
|
7374
|
-
}
|
|
7375
|
-
catch (e) {
|
|
7376
|
-
this._logger.warn(`${this.namespaceLog} Cannot subscribe to ${pattern}: ${e.message}`);
|
|
7377
|
-
return tools.maybeCallbackWithError(callback, e);
|
|
7378
|
-
}
|
|
7379
|
-
}
|
|
7380
|
-
else {
|
|
7381
|
-
this.#states.subscribeUser(pattern, callback);
|
|
7382
|
-
}
|
|
7383
|
-
}
|
|
7384
|
-
else if (pattern.startsWith(ALIAS_STARTS_WITH)) {
|
|
7385
|
-
if (!this._aliasObjectsSubscribed) {
|
|
7386
|
-
this._aliasObjectsSubscribed = true;
|
|
7387
|
-
this.#objects.subscribe(`${ALIAS_STARTS_WITH}*`);
|
|
7388
|
-
}
|
|
7389
|
-
// just read one alias Object
|
|
7390
|
-
try {
|
|
7391
|
-
const aliasObj = await this.#objects.getObject(pattern, options);
|
|
7392
|
-
if (aliasObj) {
|
|
7393
|
-
await this._addAliasSubscribe(aliasObj, pattern);
|
|
7394
|
-
return tools.maybeCallback(callback);
|
|
7395
|
-
}
|
|
7396
|
-
return tools.maybeCallback(callback);
|
|
7397
|
-
}
|
|
7398
|
-
catch (e) {
|
|
7399
|
-
this._logger.warn(`${this.namespaceLog} cannot subscribe on alias "${pattern}": ${e.message}`);
|
|
7400
|
-
}
|
|
7401
|
-
}
|
|
7402
|
-
else {
|
|
7403
|
-
this.#states.subscribeUser(pattern, callback);
|
|
7404
|
-
}
|
|
7184
|
+
const cb = callback;
|
|
7185
|
+
this.#subscriptions
|
|
7186
|
+
.subscribeForeignStates(pattern, options)
|
|
7187
|
+
.then(() => cb?.(), (err) => cb?.(err));
|
|
7405
7188
|
}
|
|
7406
7189
|
/**
|
|
7407
7190
|
* Unsubscribe for changes for given pattern
|
|
@@ -7435,102 +7218,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
7435
7218
|
if (options !== null && options !== undefined) {
|
|
7436
7219
|
Validator.assertObject(options, 'options');
|
|
7437
7220
|
}
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
if (!this.#states) {
|
|
7443
|
-
// if states is no longer existing, we do not need to unsubscribe
|
|
7444
|
-
this._logger.info(`${this.namespaceLog} unsubscrubeForeignStates not processed because States database not connected`);
|
|
7445
|
-
return tools.maybeCallbackWithError(callback, tools.ERRORS.ERROR_DB_CLOSED);
|
|
7446
|
-
}
|
|
7447
|
-
if (this.autoSubscribe && typeof pattern === 'string') {
|
|
7448
|
-
for (const autoSub of this.autoSubscribe) {
|
|
7449
|
-
if (pattern === '*' || pattern.substring(0, autoSub.length + 1) === `${autoSub}.`) {
|
|
7450
|
-
// remove this pattern from adapter list
|
|
7451
|
-
let state;
|
|
7452
|
-
try {
|
|
7453
|
-
state = await this.#states.getState(`system.adapter.${autoSub}.subscribes`);
|
|
7454
|
-
}
|
|
7455
|
-
catch {
|
|
7456
|
-
// ignore
|
|
7457
|
-
}
|
|
7458
|
-
if (!state || !state.val) {
|
|
7459
|
-
continue;
|
|
7460
|
-
}
|
|
7461
|
-
let subs;
|
|
7462
|
-
try {
|
|
7463
|
-
subs = JSON.parse(state.val);
|
|
7464
|
-
}
|
|
7465
|
-
catch {
|
|
7466
|
-
this._logger.error(`${this.namespaceLog} Cannot parse subscribes for "${autoSub}.subscribes"`);
|
|
7467
|
-
continue;
|
|
7468
|
-
}
|
|
7469
|
-
if (!tools.isObject(subs) ||
|
|
7470
|
-
!tools.isObject(subs[pattern]) ||
|
|
7471
|
-
subs[pattern][this.namespace] === undefined) {
|
|
7472
|
-
// check subs is a valid object, because it comes from state.val
|
|
7473
|
-
continue;
|
|
7474
|
-
}
|
|
7475
|
-
if (typeof subs[pattern][this.namespace] === 'number') {
|
|
7476
|
-
subs[pattern][this.namespace]--;
|
|
7477
|
-
if (subs[pattern][this.namespace] <= 0) {
|
|
7478
|
-
delete subs[pattern][this.namespace];
|
|
7479
|
-
}
|
|
7480
|
-
}
|
|
7481
|
-
else {
|
|
7482
|
-
// corrupted info, we can only delete
|
|
7483
|
-
delete subs[pattern][this.namespace];
|
|
7484
|
-
}
|
|
7485
|
-
// if no other subs are there
|
|
7486
|
-
if (!Object.keys(subs[pattern]).length) {
|
|
7487
|
-
delete subs[pattern];
|
|
7488
|
-
}
|
|
7489
|
-
this.outputCount++;
|
|
7490
|
-
await this.#states.setState(`system.adapter.${autoSub}.subscribes`, JSON.stringify(subs));
|
|
7491
|
-
}
|
|
7492
|
-
}
|
|
7493
|
-
}
|
|
7494
|
-
let aliasPattern;
|
|
7495
|
-
const promises = [];
|
|
7496
|
-
if (Array.isArray(pattern)) {
|
|
7497
|
-
// process every entry as single unsubscribe
|
|
7498
|
-
for (const _pattern of pattern) {
|
|
7499
|
-
promises.push(this.unsubscribeForeignStatesAsync(_pattern));
|
|
7500
|
-
}
|
|
7501
|
-
}
|
|
7502
|
-
else if (pattern.includes('*') || pattern.startsWith(ALIAS_STARTS_WITH)) {
|
|
7503
|
-
if (pattern === '*' || pattern.startsWith(ALIAS_STARTS_WITH)) {
|
|
7504
|
-
aliasPattern = pattern; // check all aliases
|
|
7505
|
-
if (pattern === '*') {
|
|
7506
|
-
promises.push(this.#states.unsubscribeUser(pattern));
|
|
7507
|
-
}
|
|
7508
|
-
}
|
|
7509
|
-
else {
|
|
7510
|
-
promises.push(this.#states.unsubscribeUser(pattern));
|
|
7511
|
-
}
|
|
7512
|
-
}
|
|
7513
|
-
else {
|
|
7514
|
-
promises.push(this.#states.unsubscribeUser(pattern));
|
|
7515
|
-
}
|
|
7516
|
-
if (aliasPattern) {
|
|
7517
|
-
// if pattern known, remove it from alias patterns to not subscribe to further matching aliases
|
|
7518
|
-
this.aliasPatterns.delete(aliasPattern);
|
|
7519
|
-
for (const [sourceId, alias] of this.aliases) {
|
|
7520
|
-
for (let i = alias.targets.length - 1; i >= 0; i--) {
|
|
7521
|
-
if (alias.targets[i].pattern === aliasPattern) {
|
|
7522
|
-
promises.push(this._removeAliasSubscribe(sourceId, i));
|
|
7523
|
-
}
|
|
7524
|
-
}
|
|
7525
|
-
}
|
|
7526
|
-
}
|
|
7527
|
-
await Promise.all(promises);
|
|
7528
|
-
// if no alias subscribed any longer, remove subscription
|
|
7529
|
-
if (!this.aliases.size && this._aliasObjectsSubscribed) {
|
|
7530
|
-
this._aliasObjectsSubscribed = false;
|
|
7531
|
-
this.#objects.unsubscribe(`${ALIAS_STARTS_WITH}*`);
|
|
7532
|
-
}
|
|
7533
|
-
return tools.maybeCallback(callback);
|
|
7221
|
+
const cb = callback;
|
|
7222
|
+
this.#subscriptions
|
|
7223
|
+
.unsubscribeForeignStates(pattern, options)
|
|
7224
|
+
.then(() => cb?.(), (err) => cb?.(err));
|
|
7534
7225
|
}
|
|
7535
7226
|
/**
|
|
7536
7227
|
* Subscribe for changes on all states of this instance, that pass the pattern
|
|
@@ -7554,11 +7245,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
7554
7245
|
if (options !== null && options !== undefined) {
|
|
7555
7246
|
Validator.assertObject(options, 'options');
|
|
7556
7247
|
}
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
options
|
|
7560
|
-
|
|
7561
|
-
});
|
|
7248
|
+
const cb = callback;
|
|
7249
|
+
this.#subscriptions
|
|
7250
|
+
.subscribeForeignStates(Array.isArray(pattern) ? pattern : this._utils.fixId(pattern, true), options)
|
|
7251
|
+
.then(() => cb?.(), (err) => cb?.(err));
|
|
7562
7252
|
}
|
|
7563
7253
|
/**
|
|
7564
7254
|
* Unsubscribe for changes for given pattern for own states.
|
|
@@ -7584,11 +7274,10 @@ export class AdapterClass extends EventEmitter {
|
|
|
7584
7274
|
if (options !== null && options !== undefined) {
|
|
7585
7275
|
Validator.assertObject(options, 'options');
|
|
7586
7276
|
}
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
options
|
|
7590
|
-
|
|
7591
|
-
});
|
|
7277
|
+
const cb = callback;
|
|
7278
|
+
this.#subscriptions
|
|
7279
|
+
.unsubscribeForeignStates(Array.isArray(pattern) ? pattern : this._utils.fixId(pattern, true), options)
|
|
7280
|
+
.then(() => cb?.(), (err) => cb?.(err));
|
|
7592
7281
|
}
|
|
7593
7282
|
/**
|
|
7594
7283
|
* Return plugin instance
|
|
@@ -7616,31 +7305,6 @@ export class AdapterClass extends EventEmitter {
|
|
|
7616
7305
|
Validator.assertString(name, 'name');
|
|
7617
7306
|
return this.pluginHandler.getPluginConfig(name);
|
|
7618
7307
|
}
|
|
7619
|
-
async _autoSubscribeOn() {
|
|
7620
|
-
if (!this.autoSubscribe && this.#objects) {
|
|
7621
|
-
try {
|
|
7622
|
-
// collect all
|
|
7623
|
-
const res = await this.#objects.getObjectViewAsync('system', 'instance', {
|
|
7624
|
-
startkey: 'system.adapter.',
|
|
7625
|
-
endkey: 'system.adapter.\u9999',
|
|
7626
|
-
});
|
|
7627
|
-
this.autoSubscribe = [];
|
|
7628
|
-
for (const row of res.rows) {
|
|
7629
|
-
if (row.value?.common.subscribable) {
|
|
7630
|
-
const _id = row.id.substring(15); // cut system.adapter.
|
|
7631
|
-
if (!this.autoSubscribe.includes(_id)) {
|
|
7632
|
-
this.autoSubscribe.push(_id);
|
|
7633
|
-
}
|
|
7634
|
-
}
|
|
7635
|
-
}
|
|
7636
|
-
// because of autoSubscribe
|
|
7637
|
-
await this.#objects.subscribeAsync('system.adapter.*');
|
|
7638
|
-
}
|
|
7639
|
-
catch {
|
|
7640
|
-
// ignore
|
|
7641
|
-
}
|
|
7642
|
-
}
|
|
7643
|
-
}
|
|
7644
7308
|
/**
|
|
7645
7309
|
* This method returns the list of license that can be used by this adapter
|
|
7646
7310
|
*
|
|
@@ -7769,20 +7433,24 @@ export class AdapterClass extends EventEmitter {
|
|
|
7769
7433
|
/** Time after which states regularly set by the status report expire in seconds */
|
|
7770
7434
|
const reportStatusExpirySec = Math.floor(this._config.system.statisticsInterval / 1_000) + 10;
|
|
7771
7435
|
const id = `system.adapter.${this.namespace}`;
|
|
7772
|
-
this.#states
|
|
7436
|
+
this.#states
|
|
7437
|
+
.setState(`${id}.alive`, {
|
|
7773
7438
|
val: true,
|
|
7774
7439
|
ack: true,
|
|
7775
7440
|
expire: reportStatusExpirySec,
|
|
7776
7441
|
from: id,
|
|
7777
|
-
})
|
|
7442
|
+
})
|
|
7443
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set alive state: ${e.message}`));
|
|
7778
7444
|
this.outputCount++;
|
|
7779
7445
|
if (this.connected) {
|
|
7780
|
-
this.#states
|
|
7446
|
+
this.#states
|
|
7447
|
+
.setState(`${id}.connected`, {
|
|
7781
7448
|
val: true,
|
|
7782
7449
|
ack: true,
|
|
7783
7450
|
expire: reportStatusExpirySec,
|
|
7784
7451
|
from: id,
|
|
7785
|
-
})
|
|
7452
|
+
})
|
|
7453
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set connected state: ${e.message}`));
|
|
7786
7454
|
this.outputCount++;
|
|
7787
7455
|
}
|
|
7788
7456
|
if (!this.startedInCompactMode) {
|
|
@@ -7797,47 +7465,59 @@ export class AdapterClass extends EventEmitter {
|
|
|
7797
7465
|
// timestamp: 864000000 // ms since epoch
|
|
7798
7466
|
// }
|
|
7799
7467
|
pidUsage(process.pid, (err, stats) => {
|
|
7800
|
-
// sometimes adapter is stopped, but this is still running
|
|
7801
|
-
|
|
7802
|
-
|
|
7468
|
+
// sometimes adapter is stopped, but this callback is still running: once
|
|
7469
|
+
// terminating, the states DB connection may already be closing — skip the write to
|
|
7470
|
+
// avoid a "Connection is closed" warning (setState does a read-before-write).
|
|
7471
|
+
if (!err && this && this.#states && this.#states.setState && stats && !this.terminated) {
|
|
7472
|
+
this.#states
|
|
7473
|
+
.setState(`${id}.cpu`, {
|
|
7803
7474
|
ack: true,
|
|
7804
7475
|
from: id,
|
|
7805
7476
|
val: Math.round(100 * stats.cpu) / 100,
|
|
7806
7477
|
expire: reportStatusExpirySec,
|
|
7807
|
-
})
|
|
7808
|
-
|
|
7478
|
+
})
|
|
7479
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set cpu state: ${e.message}`));
|
|
7480
|
+
this.#states
|
|
7481
|
+
.setState(`${id}.cputime`, {
|
|
7809
7482
|
ack: true,
|
|
7810
7483
|
from: id,
|
|
7811
7484
|
val: stats.ctime / 1_000,
|
|
7812
7485
|
expire: reportStatusExpirySec,
|
|
7813
|
-
})
|
|
7486
|
+
})
|
|
7487
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set cputime state: ${e.message}`));
|
|
7814
7488
|
this.outputCount += 2;
|
|
7815
7489
|
}
|
|
7816
7490
|
});
|
|
7817
7491
|
try {
|
|
7818
7492
|
//RSS is the resident set size, the portion of the process's memory held in RAM (as opposed to the swap space or the part held in the filesystem).
|
|
7819
7493
|
const mem = process.memoryUsage();
|
|
7820
|
-
this.#states
|
|
7494
|
+
this.#states
|
|
7495
|
+
.setState(`${id}.memRss`, {
|
|
7821
7496
|
val: parseFloat((mem.rss / 1048576) /* 1MB */
|
|
7822
7497
|
.toFixed(2)),
|
|
7823
7498
|
ack: true,
|
|
7824
7499
|
from: id,
|
|
7825
7500
|
expire: reportStatusExpirySec,
|
|
7826
|
-
})
|
|
7827
|
-
|
|
7501
|
+
})
|
|
7502
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set memRss state: ${e.message}`));
|
|
7503
|
+
this.#states
|
|
7504
|
+
.setState(`${id}.memHeapTotal`, {
|
|
7828
7505
|
val: parseFloat((mem.heapTotal / 1048576) /* 1MB */
|
|
7829
7506
|
.toFixed(2)),
|
|
7830
7507
|
ack: true,
|
|
7831
7508
|
from: id,
|
|
7832
7509
|
expire: reportStatusExpirySec,
|
|
7833
|
-
})
|
|
7834
|
-
|
|
7510
|
+
})
|
|
7511
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set memHeapTotal state: ${e.message}`));
|
|
7512
|
+
this.#states
|
|
7513
|
+
.setState(`${id}.memHeapUsed`, {
|
|
7835
7514
|
val: parseFloat((mem.heapUsed / 1048576) /* 1MB */
|
|
7836
7515
|
.toFixed(2)),
|
|
7837
7516
|
ack: true,
|
|
7838
7517
|
from: id,
|
|
7839
7518
|
expire: reportStatusExpirySec,
|
|
7840
|
-
})
|
|
7519
|
+
})
|
|
7520
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set memHeapUsed state: ${e.message}`));
|
|
7841
7521
|
}
|
|
7842
7522
|
catch (e) {
|
|
7843
7523
|
this._logger.warn(`${this.namespaceLog} Could not query used process memory: ${e.message}`);
|
|
@@ -7845,35 +7525,43 @@ export class AdapterClass extends EventEmitter {
|
|
|
7845
7525
|
this.outputCount += 3;
|
|
7846
7526
|
if (this.eventLoopLags.length) {
|
|
7847
7527
|
const eventLoopLag = Math.ceil(this.eventLoopLags.reduce((a, b) => a + b) / this.eventLoopLags.length);
|
|
7848
|
-
this.#states
|
|
7528
|
+
this.#states
|
|
7529
|
+
.setState(`${id}.eventLoopLag`, {
|
|
7849
7530
|
val: eventLoopLag,
|
|
7850
7531
|
ack: true,
|
|
7851
7532
|
from: id,
|
|
7852
7533
|
expire: reportStatusExpirySec,
|
|
7853
|
-
})
|
|
7534
|
+
}) // average of measured values
|
|
7535
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set eventLoopLag state: ${e.message}`));
|
|
7854
7536
|
this.eventLoopLags = [];
|
|
7855
7537
|
this.outputCount++;
|
|
7856
7538
|
}
|
|
7857
7539
|
}
|
|
7858
7540
|
this.outputCount += 3;
|
|
7859
|
-
this.#states
|
|
7541
|
+
this.#states
|
|
7542
|
+
.setState(`${id}.uptime`, {
|
|
7860
7543
|
val: parseInt(process.uptime().toFixed(), 10),
|
|
7861
7544
|
ack: true,
|
|
7862
7545
|
from: id,
|
|
7863
7546
|
expire: reportStatusExpirySec,
|
|
7864
|
-
})
|
|
7865
|
-
|
|
7547
|
+
})
|
|
7548
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set uptime state: ${e.message}`));
|
|
7549
|
+
this.#states
|
|
7550
|
+
.setState(`${id}.inputCount`, {
|
|
7866
7551
|
val: this.inputCount,
|
|
7867
7552
|
ack: true,
|
|
7868
7553
|
from: id,
|
|
7869
7554
|
expire: reportStatusExpirySec,
|
|
7870
|
-
})
|
|
7871
|
-
|
|
7555
|
+
})
|
|
7556
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set inputCount state: ${e.message}`));
|
|
7557
|
+
this.#states
|
|
7558
|
+
.setState(`${id}.outputCount`, {
|
|
7872
7559
|
val: this.outputCount,
|
|
7873
7560
|
ack: true,
|
|
7874
7561
|
from: id,
|
|
7875
7562
|
expire: reportStatusExpirySec,
|
|
7876
|
-
})
|
|
7563
|
+
})
|
|
7564
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set outputCount state: ${e.message}`));
|
|
7877
7565
|
this.inputCount = 0;
|
|
7878
7566
|
this.outputCount = 0;
|
|
7879
7567
|
}
|
|
@@ -7960,7 +7648,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
7960
7648
|
else if (this.#states?.pushLog) {
|
|
7961
7649
|
// Send it to all adapters, that required logs
|
|
7962
7650
|
for (const instanceId of this.logList) {
|
|
7963
|
-
this.#states
|
|
7651
|
+
this.#states
|
|
7652
|
+
.pushLog(instanceId, info)
|
|
7653
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot push log: ${e.message}`));
|
|
7964
7654
|
}
|
|
7965
7655
|
}
|
|
7966
7656
|
});
|
|
@@ -7986,7 +7676,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
7986
7676
|
if (this.logList.size && messages?.length && this.#states) {
|
|
7987
7677
|
for (const message of messages) {
|
|
7988
7678
|
for (const instanceId of this.logList) {
|
|
7989
|
-
this.#states
|
|
7679
|
+
this.#states
|
|
7680
|
+
.pushLog(instanceId, message)
|
|
7681
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot push log: ${e.message}`));
|
|
7990
7682
|
}
|
|
7991
7683
|
}
|
|
7992
7684
|
}
|
|
@@ -8056,7 +7748,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
8056
7748
|
this.emit('log', msg);
|
|
8057
7749
|
}
|
|
8058
7750
|
};
|
|
8059
|
-
this.#states
|
|
7751
|
+
this.#states
|
|
7752
|
+
.subscribeLog(`system.adapter.${this.namespace}`)
|
|
7753
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot subscribe to log: ${e.message}`));
|
|
8060
7754
|
}
|
|
8061
7755
|
else {
|
|
8062
7756
|
this.requireLog = isActive => {
|
|
@@ -8100,13 +7794,19 @@ export class AdapterClass extends EventEmitter {
|
|
|
8100
7794
|
}
|
|
8101
7795
|
if (!this._config.isInstall) {
|
|
8102
7796
|
// Subscribe for process exit signal
|
|
8103
|
-
this.#states
|
|
7797
|
+
this.#states
|
|
7798
|
+
.subscribe(`system.adapter.${this.namespace}.sigKill`)
|
|
7799
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot subscribe to sigKill: ${e.message}`));
|
|
8104
7800
|
// Subscribe for loglevel
|
|
8105
|
-
this.#states
|
|
7801
|
+
this.#states
|
|
7802
|
+
.subscribe(`system.adapter.${this.namespace}.logLevel`)
|
|
7803
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot subscribe to logLevel: ${e.message}`));
|
|
8106
7804
|
}
|
|
8107
7805
|
if (this._options.subscribable) {
|
|
8108
7806
|
// subscribe on if other instance wants to have states of this adapter
|
|
8109
|
-
this.#states
|
|
7807
|
+
this.#states
|
|
7808
|
+
.subscribe(`system.adapter.${this.namespace}.subscribes`)
|
|
7809
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot subscribe to subscribes: ${e.message}`));
|
|
8110
7810
|
// read actual autosubscribe requests
|
|
8111
7811
|
let state;
|
|
8112
7812
|
try {
|
|
@@ -8160,7 +7860,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
8160
7860
|
isScheduled: false,
|
|
8161
7861
|
exitCode: EXIT_CODES.ADAPTER_REQUESTED_TERMINATION,
|
|
8162
7862
|
updateAliveState: false,
|
|
8163
|
-
});
|
|
7863
|
+
}).catch(e => this._logger.error(`${this.namespaceLog} Cannot stop adapter: ${e.message}`));
|
|
8164
7864
|
}
|
|
8165
7865
|
}
|
|
8166
7866
|
}
|
|
@@ -8171,14 +7871,11 @@ export class AdapterClass extends EventEmitter {
|
|
|
8171
7871
|
if (state.val && state.val !== currentLevel && isLogLevel(newLogLevel)) {
|
|
8172
7872
|
this.overwriteLogLevel = true;
|
|
8173
7873
|
this._config.log.level = newLogLevel;
|
|
8174
|
-
for (const transport
|
|
8175
|
-
if (!Object.prototype.hasOwnProperty.call(this._logger.transports, transport)) {
|
|
8176
|
-
continue;
|
|
8177
|
-
}
|
|
7874
|
+
for (const transport of this._logger.transports) {
|
|
8178
7875
|
// set the loglevel on transport only if no loglevel was pinned in log config
|
|
8179
7876
|
// @ts-expect-error it is our own modification
|
|
8180
|
-
if (!
|
|
8181
|
-
|
|
7877
|
+
if (!transport._defaultConfigLoglevel) {
|
|
7878
|
+
transport.level = newLogLevel;
|
|
8182
7879
|
}
|
|
8183
7880
|
}
|
|
8184
7881
|
this._logger.info(`${this.namespaceLog} Loglevel changed from "${currentLevel}" to "${state.val}"`);
|
|
@@ -8189,11 +7886,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
8189
7886
|
}
|
|
8190
7887
|
this.outputCount++;
|
|
8191
7888
|
this.#states &&
|
|
8192
|
-
this.#states
|
|
7889
|
+
this.#states
|
|
7890
|
+
.setState(`system.adapter.${this.namespace}.logLevel`, {
|
|
8193
7891
|
val: currentLevel,
|
|
8194
7892
|
ack: true,
|
|
8195
7893
|
from: `system.adapter.${this.namespace}`,
|
|
8196
|
-
})
|
|
7894
|
+
})
|
|
7895
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set logLevel state: ${e.message}`));
|
|
8197
7896
|
}
|
|
8198
7897
|
}
|
|
8199
7898
|
// todo remove it as an error with log will be found
|
|
@@ -8236,29 +7935,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
8236
7935
|
// If this is messagebox
|
|
8237
7936
|
const obj = state;
|
|
8238
7937
|
if (obj) {
|
|
8239
|
-
|
|
8240
|
-
if (obj.callback?.id) {
|
|
8241
|
-
callbackObj = this.messageCallbacks.get(obj.callback.id);
|
|
8242
|
-
}
|
|
8243
|
-
// If callback stored for this request
|
|
8244
|
-
if (obj.callback?.ack && obj.callback.id && callbackObj) {
|
|
8245
|
-
// Call callback function
|
|
8246
|
-
if (typeof callbackObj.cb === 'function') {
|
|
8247
|
-
callbackObj.cb(obj.message);
|
|
8248
|
-
if (callbackObj.timer) {
|
|
8249
|
-
clearTimeout(callbackObj.timer);
|
|
8250
|
-
}
|
|
8251
|
-
this.messageCallbacks.delete(obj.callback.id);
|
|
8252
|
-
}
|
|
8253
|
-
// delete too old callbacks IDs, like garbage collector
|
|
8254
|
-
const now = Date.now();
|
|
8255
|
-
for (const [_id, callback] of this.messageCallbacks) {
|
|
8256
|
-
if (now - callback.time > 3_600_000) {
|
|
8257
|
-
this.messageCallbacks.delete(_id);
|
|
8258
|
-
}
|
|
8259
|
-
}
|
|
8260
|
-
}
|
|
8261
|
-
else if (!this._stopInProgress) {
|
|
7938
|
+
if (!this.#async.resolveReply(obj) && !this._stopInProgress) {
|
|
8262
7939
|
if (obj.command === 'clientSubscribe') {
|
|
8263
7940
|
const res = await this.uiMessagingController.registerClientSubscribeByMessage(obj);
|
|
8264
7941
|
this.sendTo(obj.from, obj.command, res, obj.callback);
|
|
@@ -8269,7 +7946,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
8269
7946
|
}
|
|
8270
7947
|
if (this._options.message) {
|
|
8271
7948
|
// Else inform about a new message the adapter
|
|
8272
|
-
this._options.message(obj);
|
|
7949
|
+
Promise.resolve(this._options.message(obj)).catch(e => this._logger.error(`${this.namespaceLog} Error in message handler: ${e.message}`));
|
|
8273
7950
|
}
|
|
8274
7951
|
this.emit('message', obj);
|
|
8275
7952
|
}
|
|
@@ -8304,37 +7981,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
8304
7981
|
}
|
|
8305
7982
|
}
|
|
8306
7983
|
}
|
|
8307
|
-
else if (!this._stopInProgress && this.adapterReady && this.
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
const uniqueTargets = new Set();
|
|
8312
|
-
for (const target of alias.targets) {
|
|
8313
|
-
const targetId = target.id;
|
|
8314
|
-
if (uniqueTargets.has(targetId)) {
|
|
8315
|
-
continue;
|
|
7984
|
+
else if (!this._stopInProgress && this.adapterReady && this.#alias.hasSource(id)) {
|
|
7985
|
+
for (const { targetId, state: aState } of this.#alias.resolveSourceChange(id, state)) {
|
|
7986
|
+
if (typeof this._options.stateChange === 'function') {
|
|
7987
|
+
Promise.resolve(this._options.stateChange(targetId, aState)).catch(e => this._logger.error(`${this.namespaceLog} Error in stateChange handler: ${e.message}`));
|
|
8316
7988
|
}
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
const aState = state
|
|
8320
|
-
? tools.formatAliasValue({
|
|
8321
|
-
sourceCommon: source,
|
|
8322
|
-
targetCommon: target,
|
|
8323
|
-
state: deepClone(state),
|
|
8324
|
-
logger: this._logger,
|
|
8325
|
-
logNamespace: this.namespaceLog,
|
|
8326
|
-
sourceId: id,
|
|
8327
|
-
targetId,
|
|
8328
|
-
})
|
|
8329
|
-
: null;
|
|
8330
|
-
if (aState || !state) {
|
|
8331
|
-
if (typeof this._options.stateChange === 'function') {
|
|
8332
|
-
this._options.stateChange(targetId, aState);
|
|
8333
|
-
}
|
|
8334
|
-
else {
|
|
8335
|
-
// emit 'stateChange' event instantly
|
|
8336
|
-
setImmediate(() => this.emit('stateChange', targetId, aState));
|
|
8337
|
-
}
|
|
7989
|
+
else {
|
|
7990
|
+
setImmediate(() => this.emit('stateChange', targetId, aState));
|
|
8338
7991
|
}
|
|
8339
7992
|
}
|
|
8340
7993
|
}
|
|
@@ -8373,7 +8026,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
8373
8026
|
return;
|
|
8374
8027
|
} // If reconnected in the meantime, do not terminate
|
|
8375
8028
|
this._logger.warn(`${this.namespaceLog} Cannot connect/reconnect to states DB. Stopping adapter.`);
|
|
8376
|
-
this._stop({ exitCode: EXIT_CODES.NO_ERROR, updateAliveState: false });
|
|
8029
|
+
this._stop({ exitCode: EXIT_CODES.NO_ERROR, updateAliveState: false }).catch(e => this._logger.error(`${this.namespaceLog} Cannot stop adapter: ${e.message}`));
|
|
8377
8030
|
}, 5000);
|
|
8378
8031
|
},
|
|
8379
8032
|
});
|
|
@@ -8440,7 +8093,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
8440
8093
|
return;
|
|
8441
8094
|
} // If reconnected in the meantime, do not terminate
|
|
8442
8095
|
this._logger.warn(`${this.namespaceLog} Cannot connect/reconnect to objects DB. Stopping adapter.`);
|
|
8443
|
-
this._stop({ exitCode: EXIT_CODES.NO_ERROR, updateAliveState: false });
|
|
8096
|
+
this._stop({ exitCode: EXIT_CODES.NO_ERROR, updateAliveState: false }).catch(e => this._logger.error(`${this.namespaceLog} Cannot stop adapter: ${e.message}`));
|
|
8444
8097
|
}, 4000);
|
|
8445
8098
|
},
|
|
8446
8099
|
change: async (id, obj) => {
|
|
@@ -8452,7 +8105,14 @@ export class AdapterClass extends EventEmitter {
|
|
|
8452
8105
|
// If desired, that adapter must be terminated
|
|
8453
8106
|
if (id === `system.adapter.${this.namespace}` && obj?.common?.enabled === false) {
|
|
8454
8107
|
this._logger.info(`${this.namespaceLog} Adapter is disabled => stop`);
|
|
8455
|
-
this._stop();
|
|
8108
|
+
this._stop().catch(e => this._logger.error(`${this.namespaceLog} Cannot stop adapter: ${e.message}`));
|
|
8109
|
+
return;
|
|
8110
|
+
}
|
|
8111
|
+
// a certificate we are using has been changed => restart to reload it
|
|
8112
|
+
if (id === CERTIFICATES_OBJECT_ID) {
|
|
8113
|
+
if (this.#async.hasRelevantCertificateChange(obj)) {
|
|
8114
|
+
this.#scheduleCertificateRestart('Changed certificate detected');
|
|
8115
|
+
}
|
|
8456
8116
|
return;
|
|
8457
8117
|
}
|
|
8458
8118
|
// update language, dateFormat and comma
|
|
@@ -8468,78 +8128,17 @@ export class AdapterClass extends EventEmitter {
|
|
|
8468
8128
|
}
|
|
8469
8129
|
// if alias
|
|
8470
8130
|
if (id.startsWith(ALIAS_STARTS_WITH)) {
|
|
8471
|
-
|
|
8472
|
-
let isNewAlias = true;
|
|
8473
|
-
for (const [sourceId, alias] of this.aliases) {
|
|
8474
|
-
const targetAlias = alias.targets.find(entry => entry.id === id);
|
|
8475
|
-
// Find entry for this alias
|
|
8476
|
-
if (targetAlias) {
|
|
8477
|
-
isNewAlias = false;
|
|
8478
|
-
// new sourceId or same
|
|
8479
|
-
if (obj?.common?.alias?.id) {
|
|
8480
|
-
// check if id.read or id
|
|
8481
|
-
const newSourceId = typeof obj.common.alias.id.read === 'string'
|
|
8482
|
-
? obj.common.alias.id.read
|
|
8483
|
-
: obj.common.alias.id;
|
|
8484
|
-
// if linked ID changed
|
|
8485
|
-
if (newSourceId !== sourceId) {
|
|
8486
|
-
await this._removeAliasSubscribe(sourceId, targetAlias);
|
|
8487
|
-
try {
|
|
8488
|
-
await this._addAliasSubscribe(obj, targetAlias.pattern);
|
|
8489
|
-
}
|
|
8490
|
-
catch (e) {
|
|
8491
|
-
this._logger.error(`${this.namespaceLog} Could not add alias subscription: ${e.message}`);
|
|
8492
|
-
}
|
|
8493
|
-
}
|
|
8494
|
-
else {
|
|
8495
|
-
// update attributes
|
|
8496
|
-
targetAlias.min = obj.common.min;
|
|
8497
|
-
targetAlias.max = obj.common.max;
|
|
8498
|
-
targetAlias.type = obj.common.type;
|
|
8499
|
-
targetAlias.alias = deepClone(obj.common.alias);
|
|
8500
|
-
}
|
|
8501
|
-
}
|
|
8502
|
-
else {
|
|
8503
|
-
// link was deleted
|
|
8504
|
-
// remove from targets array
|
|
8505
|
-
await this._removeAliasSubscribe(sourceId, targetAlias);
|
|
8506
|
-
}
|
|
8507
|
-
}
|
|
8508
|
-
}
|
|
8509
|
-
// it's a new alias, we add it to our subscription
|
|
8510
|
-
if (isNewAlias && obj) {
|
|
8511
|
-
for (const aliasPattern of this.aliasPatterns) {
|
|
8512
|
-
// check if it's in our subs range, if so add it
|
|
8513
|
-
const testPattern = aliasPattern.slice(-1) === '*'
|
|
8514
|
-
? new RegExp(tools.pattern2RegEx(aliasPattern))
|
|
8515
|
-
: aliasPattern;
|
|
8516
|
-
if ((typeof testPattern === 'string' && aliasPattern === id) ||
|
|
8517
|
-
(testPattern instanceof RegExp && testPattern.test(id))) {
|
|
8518
|
-
try {
|
|
8519
|
-
await this._addAliasSubscribe(obj, id);
|
|
8520
|
-
}
|
|
8521
|
-
catch (e) {
|
|
8522
|
-
this._logger.warn(`${this.namespaceLog} Could not add alias subscription: ${e.message}`);
|
|
8523
|
-
}
|
|
8524
|
-
break;
|
|
8525
|
-
}
|
|
8526
|
-
}
|
|
8527
|
-
}
|
|
8131
|
+
await this.#alias.handleAliasObjectChange(id, obj);
|
|
8528
8132
|
}
|
|
8529
8133
|
// process auto-subscribe adapters
|
|
8530
8134
|
if (id.startsWith('system.adapter.')) {
|
|
8531
8135
|
if (obj?.common?.subscribable) {
|
|
8532
8136
|
const _id = id.substring(15); // 'system.adapter.'.length
|
|
8533
8137
|
if (obj.common.enabled) {
|
|
8534
|
-
|
|
8535
|
-
this.autoSubscribe.push(_id);
|
|
8536
|
-
}
|
|
8138
|
+
this.#subscriptions.addSubscribableInstance(_id);
|
|
8537
8139
|
}
|
|
8538
8140
|
else {
|
|
8539
|
-
|
|
8540
|
-
if (pos !== -1) {
|
|
8541
|
-
this.autoSubscribe.splice(pos, 1);
|
|
8542
|
-
}
|
|
8141
|
+
this.#subscriptions.removeSubscribableInstance(_id);
|
|
8543
8142
|
}
|
|
8544
8143
|
}
|
|
8545
8144
|
}
|
|
@@ -8677,7 +8276,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
8677
8276
|
// if adapterState was destroyed, we should not continue
|
|
8678
8277
|
return;
|
|
8679
8278
|
}
|
|
8680
|
-
this.#states
|
|
8279
|
+
this.#states
|
|
8280
|
+
.subscribe(`system.adapter.${this.namespace}.plugins.*`)
|
|
8281
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot subscribe to plugins: ${e.message}`));
|
|
8681
8282
|
if (this._options.instance === undefined) {
|
|
8682
8283
|
if (!adapterConfig || !('common' in adapterConfig) || !adapterConfig.common.enabled) {
|
|
8683
8284
|
if (adapterConfig && 'common' in adapterConfig && adapterConfig.common.enabled !== undefined) {
|
|
@@ -8769,7 +8370,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
8769
8370
|
this.stop = params => this._stop({ ...params, isPause: false });
|
|
8770
8371
|
}
|
|
8771
8372
|
// Monitor logging state
|
|
8772
|
-
this.#states
|
|
8373
|
+
this.#states
|
|
8374
|
+
.subscribe(`${SYSTEM_ADAPTER_PREFIX}*.logging`)
|
|
8375
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot subscribe to logging: ${e.message}`));
|
|
8773
8376
|
if (typeof this._options.message === 'function' &&
|
|
8774
8377
|
// @ts-expect-error, we should infer correctly that this is an InstanceObject in this case
|
|
8775
8378
|
!isMessageboxSupported(adapterConfig.common)) {
|
|
@@ -8777,8 +8380,9 @@ export class AdapterClass extends EventEmitter {
|
|
|
8777
8380
|
// @ts-expect-error we should infer adapterConfig correctly
|
|
8778
8381
|
}
|
|
8779
8382
|
else if (isMessageboxSupported(adapterConfig.common)) {
|
|
8780
|
-
this
|
|
8781
|
-
|
|
8383
|
+
this.#states
|
|
8384
|
+
.subscribeMessage(`system.adapter.${this.namespace}`)
|
|
8385
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot subscribe to messages: ${e.message}`));
|
|
8782
8386
|
}
|
|
8783
8387
|
}
|
|
8784
8388
|
else {
|
|
@@ -8795,7 +8399,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
8795
8399
|
this.host = this.common?.host || tools.getHostName();
|
|
8796
8400
|
}
|
|
8797
8401
|
this.adapterConfig = adapterConfig;
|
|
8798
|
-
this._utils = new Validator(this.#objects, this
|
|
8402
|
+
this._utils = new Validator(this.#objects, this.namespaceLog, this._logger, this.namespace, this._namespaceRegExp);
|
|
8799
8403
|
this.log = new Log(this.namespaceLog, this._config.log.level, this._logger);
|
|
8800
8404
|
await this._createInstancesObjects(adapterConfig);
|
|
8801
8405
|
// auto oObjects
|
|
@@ -8804,19 +8408,22 @@ export class AdapterClass extends EventEmitter {
|
|
|
8804
8408
|
await this.subscribeObjectsAsync('*');
|
|
8805
8409
|
}
|
|
8806
8410
|
// initialize the system secret
|
|
8807
|
-
await this.getSystemSecret();
|
|
8411
|
+
const secret = await this.getSystemSecret();
|
|
8808
8412
|
// Decrypt all attributes of encryptedNative
|
|
8809
|
-
const promises = [];
|
|
8810
8413
|
// @ts-expect-error
|
|
8811
8414
|
if (Array.isArray(adapterConfig.encryptedNative)) {
|
|
8415
|
+
this._decryptedNativeAttributes = new Array();
|
|
8812
8416
|
// @ts-expect-error
|
|
8813
8417
|
for (const attr of adapterConfig.encryptedNative) {
|
|
8814
|
-
//
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8418
|
+
// one attribute at a time, so a value that cannot be decrypted does not stop the others
|
|
8419
|
+
try {
|
|
8420
|
+
decryptArray({ obj: this.config, secret, keys: [attr] });
|
|
8421
|
+
// only after it worked: a failed decrypt leaves the encrypted value in place, and
|
|
8422
|
+
// reading it back must decrypt it rather than hand out the cipher text
|
|
8423
|
+
this._decryptedNativeAttributes.push(attr);
|
|
8424
|
+
}
|
|
8425
|
+
catch (e) {
|
|
8426
|
+
this._logger.error(`${this.namespaceLog} Can not decrypt attribute ${attr}: ${e.message}`);
|
|
8820
8427
|
}
|
|
8821
8428
|
}
|
|
8822
8429
|
}
|
|
@@ -8827,19 +8434,19 @@ export class AdapterClass extends EventEmitter {
|
|
|
8827
8434
|
this.SUPPORTED_FEATURES.splice(idx, 1);
|
|
8828
8435
|
}
|
|
8829
8436
|
}
|
|
8830
|
-
// Wait till all attributes decrypted
|
|
8831
|
-
await Promise.all(promises);
|
|
8832
8437
|
if (!this.#states) {
|
|
8833
8438
|
// if this.adapterStates was destroyed, we should not continue
|
|
8834
8439
|
return;
|
|
8835
8440
|
}
|
|
8836
8441
|
this.outputCount++;
|
|
8837
8442
|
// set current loglevel
|
|
8838
|
-
this.#states
|
|
8443
|
+
this.#states
|
|
8444
|
+
.setState(`system.adapter.${this.namespace}.logLevel`, {
|
|
8839
8445
|
val: this._config.log.level,
|
|
8840
8446
|
ack: true,
|
|
8841
8447
|
from: `system.adapter.${this.namespace}`,
|
|
8842
|
-
})
|
|
8448
|
+
})
|
|
8449
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set logLevel state: ${e.message}`));
|
|
8843
8450
|
try {
|
|
8844
8451
|
await this.#checkObjectsWarnLimit();
|
|
8845
8452
|
}
|
|
@@ -8864,19 +8471,24 @@ export class AdapterClass extends EventEmitter {
|
|
|
8864
8471
|
this._reportInterval = setInterval(() => this._reportStatus(), this._config.system.statisticsInterval);
|
|
8865
8472
|
this._reportStatus();
|
|
8866
8473
|
const id = `system.adapter.${this.namespace}`;
|
|
8867
|
-
this.#states
|
|
8474
|
+
this.#states
|
|
8475
|
+
.setState(`${id}.compactMode`, {
|
|
8868
8476
|
ack: true,
|
|
8869
8477
|
from: id,
|
|
8870
8478
|
val: !!this.startedInCompactMode,
|
|
8871
|
-
})
|
|
8479
|
+
})
|
|
8480
|
+
.catch(e => this._logger.error(`${this.namespaceLog} Cannot set compactMode state: ${e.message}`));
|
|
8872
8481
|
this.outputCount++;
|
|
8873
8482
|
if (this.startedInCompactMode) {
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
this.#states.setState(`${id}.
|
|
8878
|
-
this.#states.setState(`${id}.
|
|
8879
|
-
this.#states.setState(`${id}.
|
|
8483
|
+
const onErr = (e) => {
|
|
8484
|
+
this._logger.error(`${this.namespaceLog} Cannot reset compact metric: ${e.message}`);
|
|
8485
|
+
};
|
|
8486
|
+
this.#states.setState(`${id}.cpu`, { ack: true, from: id, val: 0 }).catch(onErr);
|
|
8487
|
+
this.#states.setState(`${id}.cputime`, { ack: true, from: id, val: 0 }).catch(onErr);
|
|
8488
|
+
this.#states.setState(`${id}.memRss`, { val: 0, ack: true, from: id }).catch(onErr);
|
|
8489
|
+
this.#states.setState(`${id}.memHeapTotal`, { val: 0, ack: true, from: id }).catch(onErr);
|
|
8490
|
+
this.#states.setState(`${id}.memHeapUsed`, { val: 0, ack: true, from: id }).catch(onErr);
|
|
8491
|
+
this.#states.setState(`${id}.eventLoopLag`, { val: 0, ack: true, from: id }).catch(onErr);
|
|
8880
8492
|
this.outputCount += 6;
|
|
8881
8493
|
}
|
|
8882
8494
|
else {
|
|
@@ -8899,7 +8511,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
8899
8511
|
this._logger.debug(`${this.namespaceLog} Schedule restart: ${adapterConfig.common.restartSchedule}`);
|
|
8900
8512
|
this._restartScheduleJob = this._schedule.scheduleJob(adapterConfig.common.restartSchedule, () => {
|
|
8901
8513
|
this._logger.info(`${this.namespaceLog} Scheduled restart.`);
|
|
8902
|
-
this._stop({ isPause: false, isScheduled: true });
|
|
8514
|
+
this._stop({ isPause: false, isScheduled: true }).catch(e => this._logger.error(`${this.namespaceLog} Cannot stop adapter: ${e.message}`));
|
|
8903
8515
|
});
|
|
8904
8516
|
}
|
|
8905
8517
|
}
|
|
@@ -8930,13 +8542,13 @@ export class AdapterClass extends EventEmitter {
|
|
|
8930
8542
|
if (this._config.isInstall &&
|
|
8931
8543
|
(typeof this._options.install === 'function' || this.listeners('install').length)) {
|
|
8932
8544
|
if (typeof this._options.install === 'function') {
|
|
8933
|
-
this._options.install();
|
|
8545
|
+
Promise.resolve(this._options.install()).catch(e => this._logger.error(`${this.namespaceLog} Error in install handler: ${e.message}`));
|
|
8934
8546
|
}
|
|
8935
8547
|
this.emit('install');
|
|
8936
8548
|
}
|
|
8937
8549
|
else {
|
|
8938
8550
|
if (typeof this._options.ready === 'function') {
|
|
8939
|
-
this._options.ready();
|
|
8551
|
+
Promise.resolve(this._options.ready()).catch(e => this._logger.error(`${this.namespaceLog} Error in ready handler: ${e.message}`));
|
|
8940
8552
|
}
|
|
8941
8553
|
this.emit('ready');
|
|
8942
8554
|
}
|
|
@@ -8990,7 +8602,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
8990
8602
|
isScheduled: false,
|
|
8991
8603
|
exitCode: EXIT_CODES.UNCAUGHT_EXCEPTION,
|
|
8992
8604
|
updateAliveState: false,
|
|
8993
|
-
});
|
|
8605
|
+
}).catch(err => this._logger.error(`${this.namespaceLog} Cannot stop adapter: ${err.message}`));
|
|
8994
8606
|
}
|
|
8995
8607
|
catch (e) {
|
|
8996
8608
|
this._logger.error(`${this.namespaceLog} exception by stop: ${e ? e.message : e}`);
|
|
@@ -9077,7 +8689,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
9077
8689
|
});
|
|
9078
8690
|
}
|
|
9079
8691
|
return new Promise(resolve => {
|
|
9080
|
-
this._extendObjects(objs, resolve);
|
|
8692
|
+
return this._extendObjects(objs, resolve);
|
|
9081
8693
|
});
|
|
9082
8694
|
}
|
|
9083
8695
|
/**
|
|
@@ -9127,7 +8739,7 @@ export class AdapterClass extends EventEmitter {
|
|
|
9127
8739
|
return this.#states.setState(id, state);
|
|
9128
8740
|
}
|
|
9129
8741
|
async _extendObjects(tasks, callback) {
|
|
9130
|
-
if (!tasks
|
|
8742
|
+
if (!tasks?.length) {
|
|
9131
8743
|
return tools.maybeCallback(callback);
|
|
9132
8744
|
}
|
|
9133
8745
|
const task = tasks.shift();
|