iobroker.drag-indicator 2.5.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,6 +21,9 @@ e.g. you can use it for power or temperature values.
21
21
  Placeholder for the next version (at the beginning of the line):
22
22
  ### **WORK IN PROGRESS**
23
23
  -->
24
+ ### 2.6.0 (2026-04-05)
25
+ * (BenAhrdt) implement device Manager
26
+
24
27
  ### 2.5.1 (2026-02-28)
25
28
  * (BenAhrdt) update dependecies
26
29
 
@@ -0,0 +1,34 @@
1
+ {
2
+ "i18n": true,
3
+ "type": "tabs",
4
+ "tabsStyle": {
5
+ "width": "calc(100% - 100px)"
6
+ },
7
+ "items":{
8
+ "_deviceManager": {
9
+ "type": "panel",
10
+ "label": "devices",
11
+ "items": {
12
+ "_dm": {
13
+ "type": "deviceManager",
14
+ "sm": 12,
15
+ "style": {
16
+ "width": "100%",
17
+ "height": "100%",
18
+ "overflow": "hidden"
19
+ }
20
+ }
21
+ },
22
+ "style": {
23
+ "width": "100%",
24
+ "height": "100%",
25
+ "overflow": "hidden"
26
+ },
27
+ "innerStyle": {
28
+ "width": "100%",
29
+ "height": "100%",
30
+ "overflow": "hidden"
31
+ }
32
+ }
33
+ }
34
+ }
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "drag-indicator",
4
- "version": "2.5.1",
4
+ "version": "2.6.0",
5
5
  "news": {
6
+ "2.6.0": {
7
+ "en": "implement device Manager",
8
+ "de": "gerät Manager implementieren",
9
+ "ru": "реализовать Device Manager",
10
+ "pt": "implementar o gerenciador de dispositivos",
11
+ "nl": "apparaatbeheer implementeren",
12
+ "fr": "implémenter le gestionnaire de périphériques",
13
+ "it": "implementare il dispositivo Manager",
14
+ "es": "implementar dispositivo Manager",
15
+ "pl": "zaimplementuj menedżera urządzeń",
16
+ "uk": "реалізація диспетчера пристроїв",
17
+ "zh-cn": "执行设备管理器"
18
+ },
6
19
  "2.5.1": {
7
20
  "en": "update dependecies",
8
21
  "de": "aktualisierung der abhängigkeiten",
@@ -80,19 +93,6 @@
80
93
  "pl": "* Update Dependences: \"js- controller\": \"> = 5.0.19\"\nSprawdź system przed zainstalowaniem nowej wersji",
81
94
  "uk": "* Оновлення залежностей: \"js-controller\": \">=5.0.19\"\nПеревірити систему до встановлення нової версії",
82
95
  "zh-cn": "* 更新属:\"js-controller\":\">=5.0.19\"\n在安装新版本前检查您的系统"
83
- },
84
- "2.1.6": {
85
- "en": "correct changes for check and service bot",
86
- "de": "korrekte änderungen für check und service bot",
87
- "ru": "правильные изменения для проверки и обслуживания бота",
88
- "pt": "alterações corretas para check e service bot",
89
- "nl": "correcte wijzigingen voor controle en service bot",
90
- "fr": "corrects changements pour vérifier et service bot",
91
- "it": "modifiche corrette per check e service bot",
92
- "es": "cambios correctos para cheque y servicio bot",
93
- "pl": "poprawne zmiany dla bota czekowego i serwisowego",
94
- "uk": "правильні зміни для перевірки та обслуговування бота",
95
- "zh-cn": "检查和服务机器人的正确更改"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -146,9 +146,21 @@
146
146
  "compact": true,
147
147
  "connectionType": "local",
148
148
  "dataSource": "poll",
149
+ "supportedMessages": {
150
+ "deviceManager": true
151
+ },
149
152
  "adminUI": {
150
153
  "config": "json",
151
- "custom": "json"
154
+ "custom": "json",
155
+ "tab": "json"
156
+ },
157
+ "adminTab": {
158
+ "link": "jsonTab.json",
159
+ "singleton": false,
160
+ "name": {
161
+ "en": "BDCounter",
162
+ "de": "BDCounter"
163
+ }
152
164
  },
153
165
  "supportCustoms": true,
154
166
  "globalDependencies": [
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+ const lodash = require('lodash');
3
+
4
+ const { DeviceManagement } = require('@iobroker/dm-utils');
5
+
6
+ /**
7
+ * DeviceManager Class
8
+ */
9
+ class GridVisDeviceManagement extends DeviceManagement {
10
+ /**
11
+ * Initialize Class with Adapter
12
+ *
13
+ * @param adapter Adapter Reference
14
+ */
15
+ constructor(adapter) {
16
+ super(adapter);
17
+ this.adapter = adapter;
18
+ }
19
+
20
+ /**
21
+ * List all devices
22
+ *
23
+ * @param context Context of loadDevices
24
+ */
25
+ async loadDevices(context) {
26
+ context.setTotalDevices(Object.keys(this.adapter.objectStore.devices).length);
27
+ const sortedDevices = Object.fromEntries(
28
+ Object.entries(this.adapter.objectStore.devices).sort(([, a], [, b]) => {
29
+ const nameA = a.object?.common?.name?.toLowerCase() || '';
30
+ const nameB = b.object?.common?.name?.toLowerCase() || '';
31
+ return nameA.localeCompare(nameB);
32
+ }),
33
+ );
34
+ for (const [deviceId, deviceValue] of Object.entries(sortedDevices)) {
35
+ const identifier = deviceValue.object.common.desc;
36
+ const res = {
37
+ id: deviceId,
38
+ identifier:
39
+ identifier < 27
40
+ ? identifier
41
+ : `${identifier.substring(0, 13)} ... ${identifier.substring(identifier.length - 13)}`,
42
+ name:
43
+ deviceValue.object.common.name !== undefined && deviceValue.object.common.name !== ''
44
+ ? deviceValue.object.common.name
45
+ : deviceId,
46
+ hasDetails: true,
47
+ color: 'white',
48
+ backgroundColor: this.adapter.activeStates[deviceValue.object.common.desc] ? 'primary' : 'black',
49
+ icon: this.adapter.activeStates[deviceValue.object.common.desc]
50
+ ? `/adapter/${this.adapter.name}/drag-indicator.png`
51
+ : `/adapter/${this.adapter.name}/drag-indicator-inactive.png`,
52
+ };
53
+ res.customInfo = {
54
+ id: deviceId,
55
+ schema: {
56
+ type: 'panel',
57
+ items: {},
58
+ },
59
+ };
60
+ for (const [key, value] of Object.entries(deviceValue)) {
61
+ if (key === 'object') {
62
+ continue;
63
+ }
64
+ let lastIdPart = value.object._id.substring(value.object._id.lastIndexOf('.') + 1);
65
+ let card = {
66
+ name: ` ${lastIdPart}`,
67
+ };
68
+ card = lodash.merge(card, value.object.native?.card);
69
+ const preLabel = card.preLabel ?? '';
70
+ let label = '';
71
+ if (card.name) {
72
+ label = card.name;
73
+ } else if (card.label) {
74
+ label = card.label;
75
+ } else {
76
+ label = value.object._id.substring(value.object._id.lastIndexOf('.') + 1);
77
+ }
78
+ res.customInfo.schema.items[`_${value.object._id}`] = {
79
+ type: 'state',
80
+ oid: value.object._id,
81
+ foreign: true,
82
+ control: label === ' reset' ? 'button' : 'number',
83
+ label: preLabel + label,
84
+ digits: card.digits ?? 1,
85
+ };
86
+ }
87
+ const items = res.customInfo.schema.items;
88
+
89
+ const sortedEntries = Object.entries(items).sort(([, a], [, b]) => {
90
+ return a.label.localeCompare(b.label, 'de');
91
+ });
92
+
93
+ // 2. Array → Objekt
94
+ res.customInfo.schema.items = Object.fromEntries(sortedEntries);
95
+ context.addDevice(res);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * @param {string} id ID from device
101
+ * @returns {Promise<import('@iobroker/dm-utils').DeviceDetails>} return the right value
102
+ */
103
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
104
+ // @ts-expect-error
105
+ async getDeviceDetails(id) {
106
+ // eslint-disable-next-line jsdoc/check-tag-names
107
+ /** @type {Record<string, import('@iobroker/dm-utils').ConfigItemAny>} */
108
+ const sourceItems = {};
109
+ // eslint-disable-next-line jsdoc/check-tag-names
110
+ /** @type {Record<string, import('@iobroker/dm-utils').ConfigItemAny>} */
111
+ const deviceObjectItems = {};
112
+ const data = {};
113
+
114
+ sourceItems[`Header`] = {
115
+ newLine: true,
116
+ type: 'header',
117
+ text: `sourceStates`,
118
+ size: 3,
119
+ };
120
+ // Replace
121
+ const usedId = this.adapter.objectStore.devices[id].object.common.desc;
122
+ sourceItems[`source`] = {
123
+ newLine: true,
124
+ type: 'state',
125
+ control: 'number',
126
+ label: usedId,
127
+ oid: usedId,
128
+ foreign: true,
129
+ };
130
+
131
+ // Devices Object
132
+ deviceObjectItems['DeviceObjectHeader'] = {
133
+ newLine: true,
134
+ type: 'header',
135
+ text: 'DeviceObject',
136
+ size: 3,
137
+ };
138
+ deviceObjectItems['DeviceObject'] = {
139
+ type: 'text',
140
+ readOnly: true,
141
+ minRows: 30,
142
+ maxRows: 30,
143
+ };
144
+ data.DeviceObject = JSON.stringify(this.adapter.objectStore.devices[id], null, 2);
145
+
146
+ // eslint-disable-next-line jsdoc/check-tag-names
147
+ /** @type {import('@iobroker/dm-utils').JsonFormSchema} */
148
+ const schema = {
149
+ type: 'tabs',
150
+ tabsStyle: {
151
+ minWidth: 850,
152
+ },
153
+ items: {},
154
+ };
155
+ schema.items.sourceItems = {
156
+ type: 'panel',
157
+ label: 'sourceStates',
158
+ items: sourceItems,
159
+ };
160
+ schema.items.deviceObtectItems = {
161
+ type: 'panel',
162
+ label: 'deviceObject',
163
+ items: deviceObjectItems,
164
+ };
165
+ // return the schema
166
+ return { id, schema, data };
167
+ }
168
+ }
169
+
170
+ module.exports = GridVisDeviceManagement;
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Objectstore Class
5
+ */
6
+ class objectStoreClass {
7
+ /**
8
+ * Initalize Class with Adapter
9
+ *
10
+ * @param adapter Adapter Reference
11
+ */
12
+ constructor(adapter) {
13
+ this.adapter = adapter;
14
+
15
+ // Objects
16
+ this.devices = {};
17
+ this.currentIds = {};
18
+ this.startCondition = `${this.adapter.namespace}.observed_Values.`;
19
+ }
20
+
21
+ /**
22
+ * Funktion to get Devicestructure
23
+ *
24
+ */
25
+ async generateStoreObjects() {
26
+ const activeFunction = 'objectStore.js - generateDeviceObjects';
27
+ this.adapter.log.silly(`Function ${activeFunction} started.`);
28
+ try {
29
+ // Get the States
30
+ const adapterObjects = await this.adapter.getAdapterObjectsAsync();
31
+ for (const adapterObject of Object.values(adapterObjects)) {
32
+ if (adapterObject._id.startsWith(this.startCondition)) {
33
+ await this.initDeviceObject(adapterObject._id, { payload: { object: adapterObject } });
34
+ }
35
+ }
36
+ } catch (error) {
37
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
38
+ }
39
+ }
40
+
41
+ /********************************************************************************************************************************
42
+ * ******************************************************************************************************************************
43
+ * *************************************************************************************************************************** */
44
+
45
+ /**
46
+ * @param id id, for wich the structure is to build
47
+ * @param options eg. payload wich is set to last element in id
48
+ */
49
+ async initDeviceObject(id, options = {}) {
50
+ const activeFunction = 'objectStore.js - initLoraWanObject';
51
+ this.adapter.log.silly(`Function ${activeFunction} started.`);
52
+ try {
53
+ let { strip = 3, payload } = options;
54
+ // Get global values
55
+ const deviceObject = this.devices;
56
+ const idObject = this.currentIds;
57
+
58
+ const parts = id.split('.').slice(strip);
59
+ let node = deviceObject;
60
+ for (let i = 0; i < parts.length; i++) {
61
+ const key = parts[i];
62
+ const isLast = i === parts.length - 1;
63
+
64
+ if (isLast) {
65
+ if (payload !== undefined) {
66
+ // Assign object, if not present
67
+ node[key] ??= {};
68
+ idObject[id] ??= node[key];
69
+ // Assign payload entries
70
+ for (const [name, value] of Object.entries(payload)) {
71
+ node[key][name] = value;
72
+ }
73
+
74
+ // Following only type state
75
+ if (node[key].object?.type === 'state') {
76
+ // Get state, if not present
77
+ if (!node[key].state) {
78
+ if (node[key].object._id) {
79
+ const state = await this.adapter.getStateAsync(node[key].object._id);
80
+ node[key].state = state;
81
+ }
82
+ }
83
+ }
84
+ } else {
85
+ node[key] ??= {};
86
+ idObject[id] ??= node[key];
87
+ }
88
+ } else {
89
+ node[key] ??= {};
90
+ node = node[key];
91
+ }
92
+ }
93
+ } catch (error) {
94
+ this.adapter.log.error(`error at ${activeFunction}: ${error} - id: ${id}`);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @param id id, for wich the structure is to build
100
+ * @param options eg. payload wich is set to last element in id
101
+ */
102
+ async updateDeviceObject(id, options = {}) {
103
+ const activeFunction = 'objectStore.js - initLoraWanObject';
104
+ this.adapter.log.silly(`Function ${activeFunction} started.`);
105
+ try {
106
+ let { strip = 3, payload } = options;
107
+ // Get global values
108
+ const deviceObject = this.devices;
109
+ const idObject = this.currentIds;
110
+
111
+ const parts = id.split('.').slice(strip);
112
+ let node = deviceObject;
113
+ const deviceId = parts[0];
114
+ if (!deviceObject[deviceId]) {
115
+ await this.initDeviceObject(id, options);
116
+ return;
117
+ }
118
+ for (let i = 0; i < parts.length; i++) {
119
+ const key = parts[i];
120
+ const isLast = i === parts.length - 1;
121
+
122
+ if (isLast) {
123
+ if (payload !== undefined) {
124
+ // Assign object, if not present
125
+ node[key] ??= {};
126
+ idObject[id] ??= node[key];
127
+ // Assign payload entries
128
+ for (const [name, value] of Object.entries(payload)) {
129
+ node[key][name] = value;
130
+ }
131
+ } else {
132
+ node[key] ??= {};
133
+ idObject[id] ??= node[key];
134
+ }
135
+ } else {
136
+ node[key] ??= {};
137
+ node = node[key];
138
+ }
139
+ }
140
+ } catch (error) {
141
+ this.adapter.log.error(`error at ${activeFunction}: ${error} - id: ${id}`);
142
+ }
143
+ }
144
+ /********************************************************************************************************************************
145
+ * ******************************************************************************************************************************
146
+ * *************************************************************************************************************************** */
147
+ }
148
+
149
+ module.exports = objectStoreClass;
package/main.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
-
2
+ const objectStoreClass = require('./lib/modules/objectStore');
3
+ const DragindicatorDeviceManagement = require('./lib/modules/deviceManager/deviceManager');
3
4
  /*
4
5
  * Created with @iobroker/create-adapter v2.1.1
5
6
  */
@@ -8,10 +9,10 @@
8
9
  // you need to create an adapter
9
10
  const utils = require('@iobroker/adapter-core');
10
11
  const schedule = require('node-schedule');
11
- const { isDeepStrictEqual } = require('util');
12
+ const { isDeepStrictEqual } = require('node:util');
12
13
 
13
14
  // Load your modules here, e.g.:
14
- // const fs = require("fs");
15
+ // const fs = require("node:fs");
15
16
 
16
17
  class DragIndicator extends utils.Adapter {
17
18
  /**
@@ -25,7 +26,7 @@ class DragIndicator extends utils.Adapter {
25
26
  this.on('ready', this.onReady.bind(this));
26
27
  this.on('stateChange', this.onStateChange.bind(this));
27
28
  this.on('objectChange', this.onObjectChange.bind(this));
28
- // this.on("message", this.onMessage.bind(this));
29
+ this.on('message', this.onMessage.bind(this));
29
30
  this.on('unload', this.onUnload.bind(this));
30
31
 
31
32
  this.subscribecounterId = 'info.subscribedStatesCount';
@@ -52,6 +53,11 @@ class DragIndicator extends utils.Adapter {
52
53
  */
53
54
  async onReady() {
54
55
  // Initialize your adapter here
56
+ // Generate Object Store
57
+ this.objectStore = new objectStoreClass(this);
58
+ await this.objectStore.generateStoreObjects();
59
+ // Device Manager
60
+ this.deviceManagement = new DragindicatorDeviceManagement(this);
55
61
 
56
62
  //Read all states with custom configuration
57
63
  const customStateArray = await this.getObjectViewAsync('system', 'custom', {});
@@ -86,6 +92,7 @@ class DragIndicator extends utils.Adapter {
86
92
  }
87
93
 
88
94
  this.subscribeForeignObjects('*');
95
+ this.subscribeStates('*');
89
96
  this.setState(this.subscribecounterId, this.subscribecounter, true);
90
97
  }
91
98
 
@@ -124,6 +131,7 @@ class DragIndicator extends utils.Adapter {
124
131
  type: 'channel',
125
132
  common: {
126
133
  name: customInfo.channelName,
134
+ desc: id,
127
135
  },
128
136
  native: {},
129
137
  });
@@ -253,6 +261,10 @@ class DragIndicator extends utils.Adapter {
253
261
  async onObjectChange(id, obj) {
254
262
  if (obj) {
255
263
  try {
264
+ // Internal ObjectStore
265
+ if (id.startsWith(this.objectStore?.startCondition)) {
266
+ await this.objectStore?.updateDeviceObject(id, { payload: { object: obj } });
267
+ }
256
268
  if (!obj.common.custom || !obj.common.custom[this.namespace]) {
257
269
  if (this.activeStates[id]) {
258
270
  this.clearStateArrayElement(id, false);
@@ -310,6 +322,10 @@ class DragIndicator extends utils.Adapter {
310
322
  */
311
323
  async onStateChange(id, state) {
312
324
  if (state) {
325
+ // Internal ObjectStore
326
+ if (id.startsWith(this.objectStore?.startCondition)) {
327
+ await this.objectStore?.updateDeviceObject(id, { payload: { state: state } });
328
+ }
313
329
  // Check if state.val is reachable
314
330
  if (state.val !== undefined && state.val !== null) {
315
331
  // Check Changes in Foreign states
@@ -377,17 +393,23 @@ class DragIndicator extends utils.Adapter {
377
393
  // * Using this method requires "common.messagebox" property to be set to true in io-package.json
378
394
  // * @param {ioBroker.Message} obj
379
395
  // */
380
- // onMessage(obj) {
381
- // if (typeof obj === "object" && obj.message) {
382
- // if (obj.command === "send") {
383
- // // e.g. send email or pushover or whatever
384
- // this.log.debug("send command");
396
+ onMessage(obj) {
397
+ if (obj.command?.startsWith('dm:')) {
398
+ // Handled by Device Manager class itself, so ignored here
399
+ return;
400
+ }
401
+ if (typeof obj === 'object' && obj.message) {
402
+ if (obj.command === 'send') {
403
+ // e.g. send email or pushover or whatever
404
+ this.log.debug('send command');
385
405
 
386
- // // Send response in callback if required
387
- // if (obj.callback) this.sendTo(obj.from, obj.command, "Message received", obj.callback);
388
- // }
389
- // }
390
- // }
406
+ // Send response in callback if required
407
+ if (obj.callback) {
408
+ this.sendTo(obj.from, obj.command, 'Message received', obj.callback);
409
+ }
410
+ }
411
+ }
412
+ }
391
413
  }
392
414
 
393
415
  if (require.main !== module) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.drag-indicator",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "Shows the min and max of a selected value",
5
5
  "author": {
6
6
  "name": "BenAhrdt",
@@ -24,6 +24,8 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@iobroker/adapter-core": "^3.3.2",
27
+ "@iobroker/dm-utils": "^3.0.17",
28
+ "lodash": "^4.17.21",
27
29
  "node-schedule": "^2.1.1"
28
30
  },
29
31
  "devDependencies": {
@@ -34,7 +36,7 @@
34
36
  "@iobroker/adapter-dev": "^1.5.0",
35
37
  "@iobroker/eslint-config": "^2.2.0",
36
38
  "@iobroker/testing": "^5.2.2",
37
- "@types/node": "^25.0.3",
39
+ "@types/node": "^25.5.2",
38
40
  "@types/proxyquire": "^1.3.31",
39
41
  "proxyquire": "^2.1.3",
40
42
  "typescript": "~5.9.3"