@iobroker/js-controller-adapter 7.2.4 → 7.2.5

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.
@@ -388,6 +388,8 @@ export interface InternalAddChannelToEnumOptions {
388
388
  export interface SendToOptions {
389
389
  /** Method throws or calls error cb, if callback not called in time, works for single targets only */
390
390
  timeout?: number;
391
+ /** The user on whose behalf the message is sent */
392
+ user?: ioBroker.ObjectIDs.User;
391
393
  }
392
394
  export interface InternalSendToOptions {
393
395
  instanceName: string;
@@ -411,6 +413,7 @@ export interface InternalSendToHostOptions {
411
413
  command: string;
412
414
  message: any;
413
415
  callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo;
416
+ options?: SendToOptions;
414
417
  }
415
418
  export interface InternalGetStateOptions {
416
419
  id: string;
@@ -162,7 +162,7 @@ export interface AdapterClass {
162
162
  * Sends a message to a specific host or all hosts.
163
163
  */
164
164
  sendToHostAsync(hostName: string, message: ioBroker.MessagePayload): Promise<ioBroker.Message | undefined>;
165
- sendToHostAsync(hostName: string, command: string, message: ioBroker.MessagePayload): Promise<ioBroker.Message | undefined>;
165
+ sendToHostAsync(hostName: string, command: string, message: ioBroker.MessagePayload, options?: SendToOptions): Promise<ioBroker.Message | undefined>;
166
166
  /**
167
167
  * Sends a message to a specific instance or all instances of some specific adapter.
168
168
  */
@@ -722,7 +722,7 @@ export declare class AdapterClass extends EventEmitter {
722
722
  sendTo(instanceName: string, command: string, message: any, callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo, options?: SendToOptions): void;
723
723
  private _sendTo;
724
724
  sendToHost(hostName: string | null, message: any, callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo): void;
725
- sendToHost(hostName: string | null, command: string, message: any, callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo): void;
725
+ sendToHost(hostName: string | null, command: string, message: any, callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo, options?: SendToOptions): void;
726
726
  private _sendToHost;
727
727
  sendToUI(options: SendToUserInterfaceClientOptions): Promise<void>;
728
728
  registerNotification<Scope extends keyof ioBroker.NotificationScopes>(scope: Scope, category: ioBroker.NotificationScopes[Scope] | null, message: string, options?: NotificationOptions): Promise<void>;
@@ -363,7 +363,6 @@ class AdapterClass extends import_node_events.EventEmitter {
363
363
  this.readFileAsync = import_js_controller_common.tools.promisify(this.readFile, this, ["file", "mimeType"]);
364
364
  this.writeFileAsync = import_js_controller_common.tools.promisify(this.writeFile, this);
365
365
  this.fileExistsAsync = import_js_controller_common.tools.promisify(this.fileExists, this);
366
- this.sendToHostAsync = import_js_controller_common.tools.promisifyNoError(this.sendToHost, this);
367
366
  this.setStateAsync = import_js_controller_common.tools.promisify(this.setState, this);
368
367
  this.setStateChangedAsync = import_js_controller_common.tools.promisify(this.setStateChanged, this, ["id", "notChanged"]);
369
368
  this.setForeignStateAsync = import_js_controller_common.tools.promisify(this.setForeignState, this);
@@ -4711,13 +4710,27 @@ class AdapterClass extends import_node_events.EventEmitter {
4711
4710
  this.sendTo(instanceName, command, message, callback, options);
4712
4711
  });
4713
4712
  }
4713
+ /**
4714
+ * The `user` field of an outbound message, from the `user` of the send options.
4715
+ *
4716
+ * Spread into the envelope, so a send without a user leaves the field out entirely instead of
4717
+ * putting an empty one in: a receiver that checks `obj.user` must be able to tell "nobody named a
4718
+ * user" from "the user is known", and an empty string is neither.
4719
+ *
4720
+ * @param options the send options the caller passed, if any
4721
+ */
4722
+ #userOf(options) {
4723
+ const user = options?.user;
4724
+ return typeof user === "string" && user ? { user } : {};
4725
+ }
4714
4726
  async _sendTo(_options) {
4715
4727
  const { command, message, callback, options } = _options;
4716
4728
  let { instanceName } = _options;
4717
4729
  const obj = {
4718
4730
  command,
4719
4731
  message,
4720
- from: `system.adapter.${this.namespace}`
4732
+ from: `system.adapter.${this.namespace}`,
4733
+ ...this.#userOf(options)
4721
4734
  };
4722
4735
  if (!instanceName) {
4723
4736
  return import_js_controller_common.tools.maybeCallbackWithError(callback, "No instanceName provided or not a string");
@@ -4817,8 +4830,10 @@ class AdapterClass extends import_node_events.EventEmitter {
4817
4830
  * if (!result) adapter.log.error('No response received');
4818
4831
  * }
4819
4832
  * ```
4833
+ * @param options optional send options: a `timeout` for the answer, or the `user` this message is
4834
+ * sent on behalf of - the host sees it as `obj.user`
4820
4835
  */
4821
- sendToHost(hostName, command, message, callback) {
4836
+ sendToHost(hostName, command, message, callback, options) {
4822
4837
  if (typeof message === "undefined") {
4823
4838
  message = command;
4824
4839
  command = "send";
@@ -4830,17 +4845,57 @@ class AdapterClass extends import_node_events.EventEmitter {
4830
4845
  if (!import_js_controller_common.tools.isObject(callback)) {
4831
4846
  import_validator.Validator.assertOptionalCallback(callback, "callback");
4832
4847
  }
4848
+ if (options !== void 0) {
4849
+ import_validator.Validator.assertObject(options, "options");
4850
+ }
4833
4851
  return this._sendToHost({
4834
4852
  hostName,
4835
4853
  command,
4836
4854
  message,
4855
+ options,
4837
4856
  callback
4838
4857
  });
4839
4858
  }
4859
+ /**
4860
+ * Async version of sendToHost
4861
+ *
4862
+ * Resolves with whatever the host answered - like the promisified version it replaces, an error
4863
+ * of the host reaches the caller as the resolved value, and that is left alone: an adapter that
4864
+ * awaits this today must not start seeing rejections.
4865
+ *
4866
+ * It rejects in one case: a `timeout` was asked for in the send options and elapsed, which is
4867
+ * how `sendToAsync` reports the same thing. Whoever passes that option is writing against an
4868
+ * option that did not exist before and can expect it; whoever passes none keeps the old
4869
+ * behaviour, including a host that never answers and a caller that waits for ever.
4870
+ *
4871
+ * @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.
4872
+ * @param command command name. One of: "cmdExec", "getRepository", "getInstalled", ...
4873
+ * @param message object that will be given as argument for request
4874
+ * @param options optional send options: a `timeout` for the answer, or the `user` the message is sent for
4875
+ */
4876
+ sendToHostAsync(hostName, command, message, options) {
4877
+ const timeout = options?.timeout;
4878
+ return new Promise((resolve, reject) => {
4879
+ this.sendToHost(
4880
+ hostName,
4881
+ command,
4882
+ message,
4883
+ // The answer of a host travels through the states database and is therefore never an
4884
+ // `Error` instance; one can only come from here, and only where a budget was named.
4885
+ (result) => timeout && result instanceof Error ? reject(result) : resolve(result),
4886
+ options
4887
+ );
4888
+ });
4889
+ }
4840
4890
  async _sendToHost(_options) {
4841
- const { command, message, callback } = _options;
4891
+ const { command, message, callback, options } = _options;
4842
4892
  let { hostName } = _options;
4843
- const obj = { command, message, from: `system.adapter.${this.namespace}` };
4893
+ const obj = {
4894
+ command,
4895
+ message,
4896
+ from: `system.adapter.${this.namespace}`,
4897
+ ...this.#userOf(options)
4898
+ };
4844
4899
  if (!this.#states) {
4845
4900
  this._logger.info(`${this.namespaceLog} sendToHost not processed because States database not connected`);
4846
4901
  return import_js_controller_common.tools.maybeCallbackWithError(callback, import_js_controller_common.tools.ERRORS.ERROR_DB_CLOSED);
@@ -4889,7 +4944,18 @@ class AdapterClass extends import_node_events.EventEmitter {
4889
4944
  if (this._callbackId >= 4294967295) {
4890
4945
  this._callbackId = 1;
4891
4946
  }
4892
- this.messageCallbacks.set(obj.callback.id, { cb: callback, time: Date.now() });
4947
+ const callbackId = obj.callback.id;
4948
+ let timer;
4949
+ if (options?.timeout) {
4950
+ timer = setTimeout(() => {
4951
+ const callbackObj = this.messageCallbacks.get(callbackId);
4952
+ if (callbackObj) {
4953
+ callbackObj.cb(new Error("Timeout exceeded"));
4954
+ this.messageCallbacks.delete(callbackId);
4955
+ }
4956
+ }, options.timeout);
4957
+ }
4958
+ this.messageCallbacks.set(callbackId, { cb: callback, time: Date.now(), timer });
4893
4959
  } else {
4894
4960
  obj.callback = callback;
4895
4961
  obj.callback.ack = true;