@iobroker/dm-utils 1.0.15 → 2.0.1

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/README.md CHANGED
@@ -73,6 +73,14 @@ the `DeviceManagement` implementation's `handleXxxAction()` is called, and the a
73
73
  The communication between the `ioBroker.device-manager` tab and the adapter happens through `sendTo`.
74
74
 
75
75
  **IMPORTANT:** make sure your adapter doesn't handle `sendTo` messages starting with `dm:`, otherwise the communication will not work.
76
+ - Use for Example this on the top of your onMessage Methode:
77
+
78
+ ```js
79
+ if (obj.command?.startsWith('dm:')) {
80
+ // Handled by Device Manager class itself, so ignored here
81
+ return;
82
+ }
83
+ ```
76
84
 
77
85
  ### Access adapter methods
78
86
 
@@ -116,12 +124,11 @@ Every array entry is an object of type `DeviceInfo` which has the following prop
116
124
 
117
125
  - `id` (string): a unique (human-readable) identifier of the device (it must be unique for your adapter instance only)
118
126
  - `name` (string or translations): the human-readable name of this device
119
- - `status` (optional): the current status of the device, which can be one of:
120
- - `"disconnected"`
121
- - `"connected"`
122
- - an object containing:
123
- - `icon` (string): an icon depicting the status of the device (see below for details)
124
- - `description` (string, optional): a text that will be shown as a tooltip on the status
127
+ - `status` (optional): the current status of the device, which has to be an object containing:
128
+ - `connection` (string): alowed values are: `"connected"` / `"disconnected"`
129
+ - `rssi` (number): rssi value of the connection
130
+ - `battery` (boolean / number): if boolean: false: Battery empty. If number: battery level of the device (shows also a battery symbol at card)
131
+ - `warning` (boolean / string): if boolean: true indicates a warning. If string: shows also the warning with mouseover
125
132
  - `actions` (array, optional): an array of actions that can be performed on the device; each object contains:
126
133
  - `id` (string): unique identifier to recognize an action (never shown to the user)
127
134
  - `icon` (string): an icon shown on the button (see below for details)
@@ -129,19 +136,26 @@ Every array entry is an object of type `DeviceInfo` which has the following prop
129
136
  - `disabled` (boolean, optional): if set to `true`, the button can't be clicked but is shown to the user
130
137
  - `hasDetails` (boolean, optional): if set to `true`, the row of the device can be expanded and details are shown below
131
138
 
139
+ Possible strings for device icons are here: [TYPE ICONS](https://github.com/ioBroker/adapter-react-v5/blob/main/src/Components/DeviceType/DeviceTypeIcon.tsx#L68)
140
+ <br/>
141
+ Possible strings for action icons are here: [ACTION NAMES](https://github.com/ioBroker/dm-gui-components/blob/main/src/Utils.tsx#L128)
142
+ <br/>
143
+ Possible strings for configuration icons are here: [CONFIGURATION TYPES](https://github.com/ioBroker/dm-utils/blob/b3e54ecfaedd6a239beec59c5deb8117d1d59d7f/src/types/common.ts#L110)
144
+ <br/>
132
145
  ### `getInstanceInfo()`
133
146
 
134
147
  This method allows the device manager tab to gather some general information about the instance. It is called when the user opens the tab.
135
148
 
136
149
  If you override this method, the returned object must contain:
137
150
 
138
- - `apiVersion` (string): the supported API version; must currently always be `"v1"`
151
+ - `apiVersion` (string): the supported API version; must be `"v1"` or `"v2"` (if "backend to GUI communication" is used or IDs instead of values)
139
152
  - `actions` (array, optional): an array of actions that can be performed on the instance; each object contains:
140
153
  - `id` (string): unique identifier to recognize an action (never shown to the user)
141
154
  - `icon` (string): an icon shown on the button (see below for details)
142
155
  - `title` (string): the title shown next to the icon on the button
143
156
  - `description` (string, optional): a text that will be shown as a tooltip on the button
144
157
  - `disabled` (boolean, optional): if set to `true`, the button can't be clicked but is shown to the user
158
+ - `communicationStateId` (string) (optional): the ID of the state that is used by backend for communication with front-end (only API v2)
145
159
 
146
160
  ### `getDeviceDetails(id: string)`
147
161
 
@@ -295,14 +309,48 @@ This method returns a promise that resolves to a `ProgressDialog` object.
295
309
  - `label` (string, optional): change the label to the right of the progress bar
296
310
  - `close()`
297
311
  - Closes the progress dialog (and allows you to open other dialogs)
312
+
313
+ ### `sendCommandToGui(command: BackendToGuiCommand)`
314
+
315
+ Sends command to GUI to add/update/delete devices or to update the status of device.
316
+
317
+ **It is suggested** to use the state's ID directly in DeviceInfo structure instead of sending the command every time to GUI on status update.
318
+
319
+ See example below:
320
+ ```ts
321
+ class MyAdapterDeviceManagement extends DeviceManagement<MyAdapter> {
322
+ protected listDevices(): RetVal<DeviceInfo[]>{
323
+ const deviceInfo: DeviceInfo = {
324
+ id: 'uniqieID',
325
+ name: 'My device',
326
+ icon: 'node', // find possible icons here: https://github.com/ioBroker/adapter-react-v5/blob/main/src/Components/DeviceType/DeviceTypeIcon.tsx#L68
327
+ manufacturer: { objectId: 'uniqieID', property: 'native.manufacturer' },
328
+ model: { objectId: 'uniqieID', property: 'native.model' },
329
+ status: {
330
+ battery: { stateId: 'uniqieID.DevicePower0.BatteryPercent' },
331
+ connection: { stateId: 'uniqieID.online', mapping: {'true': 'connected', 'false': 'disconnected'} },
332
+ rssi: { stateId: 'uniqieID.rssi' },
333
+ },
334
+ hasDetails: true,
335
+ };
336
+ return [deviceInfo];
337
+ }
338
+ }
339
+ ```
298
340
 
299
341
  <!--
300
342
  Placeholder for the next version (at the beginning of the line):
301
343
  ### **WORK IN PROGRESS**
302
344
  -->
303
345
  ## Changelog
304
- ### 1.0.15 (2026-01-02)
346
+ ### 2.0.1 (2026-01-28)
347
+ * (@GermanBluefox) BREAKING: Admin/GUI must have version 9 (or higher) of `dm-gui-components`
348
+ * (@GermanBluefox) Added types to update status of device directly from state
349
+ * (@GermanBluefox) Added backend to GUI communication possibility
350
+
351
+ ### 1.0.16 (2026-01-02)
305
352
  * (@GermanBluefox) Added `ignoreApplyDisabled` flag
353
+ * (@GermanBluefox) Added `update` icon
306
354
 
307
355
  ### 1.0.13 (2025-10-21)
308
356
  * (@GermanBluefox) Updated packages
@@ -2,13 +2,16 @@ import type { AdapterInstance } from '@iobroker/adapter-core';
2
2
  import type { ActionContext } from './ActionContext';
3
3
  import type { ProgressDialog } from './ProgressDialog';
4
4
  import { type DeviceDetails, type DeviceInfo, type ErrorResponse, type InstanceDetails, type JsonFormData, type JsonFormSchema, type RefreshResponse, type RetVal, type ActionButton } from './types';
5
- import type { ControlState } from './types/base';
5
+ import type { BackendToGuiCommand, ControlState } from './types/base';
6
6
  export declare abstract class DeviceManagement<T extends AdapterInstance = AdapterInstance> {
7
7
  protected readonly adapter: T;
8
8
  private instanceInfo?;
9
9
  private devices?;
10
+ private readonly communicationStateId;
10
11
  private readonly contexts;
11
- constructor(adapter: T);
12
+ constructor(adapter: T, communicationStateId?: string | boolean);
13
+ private ensureCommunicationState;
14
+ protected sendCommandToGui(command: BackendToGuiCommand): Promise<void>;
12
15
  protected get log(): ioBroker.Log;
13
16
  protected getInstanceInfo(): RetVal<InstanceDetails>;
14
17
  protected abstract listDevices(): RetVal<DeviceInfo[]>;
@@ -3,16 +3,55 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MessageContext = exports.DeviceManagement = void 0;
4
4
  const types_1 = require("./types");
5
5
  class DeviceManagement {
6
- constructor(adapter) {
6
+ constructor(adapter, communicationStateId) {
7
7
  this.adapter = adapter;
8
8
  this.contexts = new Map();
9
9
  adapter.on('message', this.onMessage.bind(this));
10
+ if (communicationStateId === true) {
11
+ // use standard ID `info.deviceManager`
12
+ this.communicationStateId = 'info.deviceManager';
13
+ }
14
+ else if (communicationStateId) {
15
+ this.communicationStateId = communicationStateId;
16
+ }
17
+ if (this.communicationStateId) {
18
+ this.ensureCommunicationState().catch(e => this.log().error(`Cannot initialize communication state: ${e}`));
19
+ }
20
+ }
21
+ async ensureCommunicationState() {
22
+ let stateObj = await this.adapter.getObjectAsync(this.communicationStateId);
23
+ if (!stateObj) {
24
+ stateObj = {
25
+ _id: this.communicationStateId,
26
+ type: 'state',
27
+ common: {
28
+ expert: true,
29
+ name: 'Communication with GUI for device manager',
30
+ type: 'string',
31
+ role: 'state',
32
+ def: '',
33
+ read: true,
34
+ write: false,
35
+ },
36
+ native: {},
37
+ };
38
+ await this.adapter.setObjectAsync(this.communicationStateId, stateObj);
39
+ }
40
+ }
41
+ async sendCommandToGui(command) {
42
+ if (this.communicationStateId) {
43
+ await this.adapter.setStateAsync(this.communicationStateId, JSON.stringify(command), true);
44
+ }
45
+ else {
46
+ throw new Error('Communication state not found');
47
+ }
10
48
  }
11
49
  get log() {
12
50
  return this.adapter.log;
13
51
  }
14
52
  getInstanceInfo() {
15
- return { apiVersion: 'v1' };
53
+ // Overload this method if your adapter does not use BackendToGui communication and States/Objects in DeviceInfo
54
+ return { apiVersion: 'v2', communicationStateId: this.communicationStateId || undefined };
16
55
  }
17
56
  getDeviceDetails(id) {
18
57
  return { id, schema: {} };
@@ -1,4 +1,4 @@
1
- import type { ActionContext, ConfigConnectionType, ErrorResponse, MessageContext } from '..';
1
+ import type { ActionContext, ConfigConnectionType, ErrorResponse, MessageContext, ValueOrObject, ValueOrState, ValueOrStateOrObject } from '..';
2
2
  import type { ApiVersion, DeviceRefresh, DeviceStatus, RetVal } from './common';
3
3
  type ActionType = 'api' | 'adapter';
4
4
  export type Color = 'primary' | 'secondary' | (string & {});
@@ -97,33 +97,36 @@ export interface DeviceAction<T extends ActionType = 'api'> extends ActionBase<T
97
97
  }>;
98
98
  }
99
99
  export interface InstanceDetails<T extends ActionType = 'api'> {
100
+ /** API Version: 1 - till 2025 (including), 2 - from 2026 */
100
101
  apiVersion: ApiVersion;
101
102
  actions?: InstanceAction<T>[];
103
+ /** ID of state used for communication with GUI */
104
+ communicationStateId?: string;
102
105
  }
103
106
  export interface DeviceInfo<T extends ActionType = 'api'> {
104
107
  /** ID of the action. Should be unique only in one adapter. Other adapters could have same names */
105
108
  id: string;
106
109
  /** Name of the device. It will be shown in the card header */
107
- name: ioBroker.StringOrTranslated;
110
+ name: ValueOrObject<ioBroker.StringOrTranslated>;
108
111
  /** base64 or url icon for device card */
109
- icon?: string;
110
- manufacturer?: ioBroker.StringOrTranslated;
111
- model?: ioBroker.StringOrTranslated;
112
+ icon?: ValueOrState<string>;
113
+ manufacturer?: ValueOrStateOrObject<ioBroker.StringOrTranslated>;
114
+ model?: ValueOrStateOrObject<ioBroker.StringOrTranslated>;
112
115
  /** Color or 'primary', 'secondary' for the text in the card header */
113
- color?: Color;
116
+ color?: ValueOrState<Color>;
114
117
  /** Background color of card header (you can use primary, secondary or color rgb value or hex) */
115
- backgroundColor?: Color;
118
+ backgroundColor?: ValueOrState<Color>;
116
119
  status?: DeviceStatus | DeviceStatus[];
117
120
  /** Connection type, how the device is connected */
118
- connectionType?: ConfigConnectionType;
121
+ connectionType?: ValueOrStateOrObject<ConfigConnectionType>;
119
122
  /** If this flag is true or false, the according indication will be shown. Additionally, if ACTIONS.ENABLE_DISABLE is implemented, this action will be sent to backend by clicking on this indication */
120
- enabled?: boolean;
123
+ enabled?: ValueOrState<boolean>;
121
124
  /** List of actions on the card */
122
125
  actions?: DeviceAction<T>[];
123
126
  /** List of controls on the card. The difference of controls and actions is that the controls can show status (e.g. on/off) and can work directly with states */
124
127
  controls?: DeviceControl<T>[];
125
128
  /** If true, the button `more` will be shown on the card and called `dm:deviceDetails` action to get the details */
126
- hasDetails?: boolean;
129
+ hasDetails?: ValueOrStateOrObject<boolean>;
127
130
  /** Device type for grouping */
128
131
  group?: {
129
132
  key: string;
@@ -131,4 +134,30 @@ export interface DeviceInfo<T extends ActionType = 'api'> {
131
134
  icon?: string;
132
135
  };
133
136
  }
137
+ export interface BackendToGuiCommandDeviceInfoUpdate {
138
+ /** Used for updating and for adding new device */
139
+ command: 'infoUpdate';
140
+ /** Device ID */
141
+ deviceId: string;
142
+ /** Backend can send directly new information about device to avoid extra request from GUI */
143
+ info?: DeviceInfo;
144
+ }
145
+ export interface BackendToGuiCommandDeviceStatusUpdate {
146
+ /** Status of device was updated */
147
+ command: 'statusUpdate';
148
+ /** Device ID */
149
+ deviceId: string;
150
+ /** Backend can send directly new status to avoid extra request from GUI */
151
+ status?: DeviceStatus;
152
+ }
153
+ export interface BackendToGuiCommandDeviceDelete {
154
+ /** Device was deleted */
155
+ command: 'delete';
156
+ deviceId: string;
157
+ }
158
+ export interface BackendToGuiCommandAllUpdate {
159
+ /** Read ALL information about all devices anew */
160
+ command: 'all';
161
+ }
162
+ export type BackendToGuiCommand = BackendToGuiCommandDeviceInfoUpdate | BackendToGuiCommandDeviceStatusUpdate | BackendToGuiCommandDeviceDelete | BackendToGuiCommandAllUpdate;
134
163
  export {};
@@ -1,10 +1,23 @@
1
- export type ApiVersion = 'v1';
1
+ export type ApiVersion = 'v1' | 'v2';
2
2
  export type ConfigConnectionType = 'lan' | 'wifi' | 'bluetooth' | 'thread' | 'z-wave' | 'zigbee' | 'other';
3
+ export type ValueOrObject<T> = T | {
4
+ objectId: string;
5
+ property: string;
6
+ };
7
+ export type ValueOrState<T, M = {
8
+ [value: string | number]: string;
9
+ }> = T | {
10
+ stateId: string;
11
+ mapping?: M;
12
+ };
13
+ export type ValueOrStateOrObject<T> = T | ValueOrObject<T> | ValueOrState<T>;
3
14
  export type DeviceStatus = 'connected' | 'disconnected' | {
4
- battery?: number | boolean | 'charging' | string;
5
- connection?: 'connected' | 'disconnected';
6
- rssi?: number;
7
- warning?: ioBroker.StringOrTranslated | boolean;
15
+ battery?: ValueOrState<number | boolean | 'charging' | string>;
16
+ connection?: ValueOrState<'connected' | 'disconnected', {
17
+ [value: string]: 'connected' | 'disconnected';
18
+ }>;
19
+ rssi?: ValueOrState<number>;
20
+ warning?: ValueOrState<ioBroker.StringOrTranslated | boolean>;
8
21
  };
9
22
  export type DeviceRefresh = 'device' | 'instance' | false | true;
10
23
  export type RefreshResponse = {
@@ -28,7 +41,7 @@ interface ObjectBrowserCustomFilter {
28
41
  }
29
42
  export type ObjectBrowserType = 'state' | 'instance' | 'channel' | 'device' | 'chart';
30
43
  export type ConfigItemType = 'accordion' | 'alive' | 'autocomplete' | 'autocompleteSendTo' | 'certCollection' | 'certificate' | 'certificates' | 'checkDocker' | 'checkLicense' | 'checkbox' | 'chips' | 'color' | 'coordinates' | 'cron' | 'custom' | 'datePicker' | 'deviceManager' | 'divider' | 'file' | 'fileSelector' | 'func' | 'header' | 'image' | 'imageSendTo' | 'infoBox' | 'instance' | 'interface' | 'ip' | 'jsonEditor' | 'language' | 'license' | 'number' | 'oauth2' | 'objectId' | 'panel' | 'password' | 'pattern' | 'port' | 'qrCode' | 'room' | 'select' | 'selectSendTo' | 'sendto' | 'setState' | 'slider' | 'state' | 'staticImage' | 'staticInfo' | 'staticLink' | 'staticText' | 'table' | 'tabs' | 'text' | 'textSendTo' | 'timePicker' | 'topic' | 'user' | 'uuid';
31
- export type ConfigIconType = 'add' | 'backlight' | 'book' | 'delete' | 'dimmer' | 'edit' | 'error' | 'group' | 'help' | 'identify' | 'info' | 'light' | 'lines' | 'next' | 'pair' | 'pause' | 'play' | 'previous' | 'qrcode' | 'refresh' | 'search' | 'send' | 'settings' | 'socket' | 'stop' | 'unpair' | 'upload' | 'user' | 'warning' | 'web' | string;
44
+ export type ConfigIconType = 'add' | 'backlight' | 'book' | 'delete' | 'dimmer' | 'edit' | 'error' | 'group' | 'help' | 'identify' | 'info' | 'light' | 'lines' | 'next' | 'pair' | 'pause' | 'play' | 'previous' | 'qrcode' | 'refresh' | 'search' | 'send' | 'settings' | 'socket' | 'stop' | 'unpair' | 'update' | 'upload' | 'user' | 'warning' | 'web' | string;
32
45
  export interface ConfigItemConfirmData {
33
46
  condition: string;
34
47
  text?: ioBroker.StringOrTranslated;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iobroker/dm-utils",
3
- "version": "1.0.15",
3
+ "version": "2.0.1",
4
4
  "description": "ioBroker Device Manager utilities for backend",
5
5
  "main": "build/index.js",
6
6
  "publishConfig": {