iobroker.lorawan 1.18.63 → 1.19.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
@@ -23,6 +23,10 @@ For now there is documentation in English here: https://wiki.hafenmeister.de
23
23
  Placeholder for the next version (at the beginning of the line):
24
24
  ### **WORK IN PROGRESS**
25
25
  -->
26
+ ### 1.19.0 (2026-01-03)
27
+ * (BenAhrdt) remove Bridgetype Smarthome
28
+ * (BenAhrdt) add firt possibility to generate devices from Bridge to Iob
29
+
26
30
  ### 1.18.63 (2026-01-01)
27
31
  * (BenAhrdt) return to await calls in checkAll (bridge)
28
32
 
@@ -751,8 +751,7 @@
751
751
  "tooltip": "BridgeTypeTooltip",
752
752
  "options": [
753
753
  {"label":"BridgeTypeOff","value":"off"},
754
- {"label":"BridgeTypeHA","value":"HA"},
755
- {"label":"BridgeTypeSH","value":"SH"}
754
+ {"label":"BridgeTypeHA","value":"HA"}
756
755
  ],
757
756
  "default": "off",
758
757
  "xs": 12,
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "lorawan",
4
- "version": "1.18.63",
4
+ "version": "1.19.0",
5
5
  "news": {
6
+ "1.19.0": {
7
+ "en": "remove Bridgetype Smarthome\nadd firt possibility to generate devices from Bridge to Iob",
8
+ "de": "bridgetype Smarthome entfernen\nfügen Sie firt Möglichkeit, Geräte von Bridge zu Iob zu generieren",
9
+ "ru": "скачать Bridgetype Smarthome\nдобавьте возможность создания устройств от Bridge до Iob",
10
+ "pt": "remover o tipo de ponte Smarthome\nadicionar a possibilidade inicial de gerar dispositivos de Bridge para Iob",
11
+ "nl": "verwijderen Bridgetype Smarthome\nfirt mogelijkheid toevoegen om apparaten van brug aan Iob te genereren",
12
+ "fr": "supprimer Bridgetype Smarthome\najouter la possibilité de firt pour générer des appareils de Bridge à Iob",
13
+ "it": "rimuovere Bridgetype Smarthome\naggiungere firt possibilità di generare dispositivi da Bridge a Iob",
14
+ "es": "eliminar Bridgetype Smarthome\nañadir firt posibilidad de generar dispositivos desde Bridge a Iob",
15
+ "pl": "usuń Bridgetype Smarthome\ndodać firt możliwość generowania urządzeń z Bridge do Iob",
16
+ "uk": "видалити Bridgetype Smarthome\nдо Iob",
17
+ "zh-cn": "删除桥型 Smarthome\n从 Bridge 添加 firt 生成设备的可能性"
18
+ },
6
19
  "1.18.63": {
7
20
  "en": "return to await calls in checkAll (bridge)",
8
21
  "de": "zurück zu warten Anrufe im Check Alle (Brücke)",
@@ -80,19 +93,6 @@
80
93
  "pl": "serializuj i buforuj wiadomości konfiguracyjne",
81
94
  "uk": "послідовність і кешування конфігураційних повідомлень",
82
95
  "zh-cn": "序列化和缓存配置信件"
83
- },
84
- "1.18.57": {
85
- "en": "cache Infos in config for more performance",
86
- "de": "cache Infos in config für mehr Leistung",
87
- "ru": "cache Infos in Config для повышения производительности",
88
- "pt": "informações de cache em configuração para mais desempenho",
89
- "nl": "cache-informatie in configuratie voor meer prestaties",
90
- "fr": "cache Infos en configuration pour plus de performances",
91
- "it": "cache Infos in config per maggiori prestazioni",
92
- "es": "cache Infos en config para más rendimiento",
93
- "pl": "cache Infos w konfiguracji dla większej wydajności",
94
- "uk": "cache Infos в конфігурації для більшої продуктивності",
95
- "zh-cn": "为更多性能在配置中缓存信息"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -1,4 +1,5 @@
1
1
  const bridgeMqttClientClass = require('./bridgeMqttclient');
2
+ const bridgeDeviceHandlerClass = require('./bridgeDeviceHandler');
2
3
  const schedule = require('node-schedule');
3
4
  /*
4
5
  Also er published irgendwie nicht den Mode => und es kommt virtual_Mode nicht subcribed....
@@ -18,6 +19,7 @@ class bridgeClass {
18
19
  * ******************************************************************/
19
20
 
20
21
  this.bridgeMqttClient = new bridgeMqttClientClass(this.adapter, this.adapter.config);
22
+ this.bridgeDeviceHandler = new bridgeDeviceHandlerClass(this.adapter);
21
23
 
22
24
  // Structure of actual vaulues in Bridge (till las start of Adapter)
23
25
  this.CheckedIds = {};
@@ -29,7 +31,6 @@ class bridgeClass {
29
31
  this.Notifications = {};
30
32
  this.BridgeDiscoveryPrefix = {
31
33
  HA: 'homeassistant/',
32
- SH: 'smarthome/',
33
34
  };
34
35
  this.ForeignBridgeMembers = {};
35
36
  this.MinTime = 100; // ms between publish and subscribe same value
@@ -289,6 +290,9 @@ class bridgeClass {
289
290
  // Special DataExchange
290
291
  if (this.SubscribedTopics[topic].dataExchange) {
291
292
  if (typeof message === 'object') {
293
+ // Call the BridgeDeviceHandler
294
+ await this.bridgeDeviceHandler.generateDeviceStructure(message);
295
+ // Stringify for set State
292
296
  message = JSON.stringify(message);
293
297
  }
294
298
  await this.adapter.setState(
@@ -0,0 +1,373 @@
1
+ /**
2
+ * class to handle incomming messages from Bridge
3
+ */
4
+ class bridgeDeviceHandlerClass {
5
+ /**
6
+ * @param adapter data of the adapter (eg. for logging)
7
+ */
8
+ constructor(adapter) {
9
+ this.adapter = adapter;
10
+ this.basefolder = 'bridge.devices';
11
+ this.adapter.extendObject(this.basefolder, {
12
+ type: 'folder',
13
+ common: { name: 'Devices recieved from Bridge' },
14
+ native: {},
15
+ });
16
+ }
17
+
18
+ // Generate Structure of incomming Data
19
+ /**
20
+ * @param message message from bridge to generate devices (eg.)
21
+ */
22
+ async generateDeviceStructure(message) {
23
+ const activeFunction = 'bridgeDeviceHandler.js - generateDeviceStructure';
24
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
25
+ try {
26
+ // Query for Entity
27
+ if (message.entities) {
28
+ for (const entity of Object.values(message.entities)) {
29
+ const entityInfo = this.generateStructure(entity);
30
+ await this.adapter.extendObject(entityInfo?.device.id, {
31
+ type: 'device',
32
+ common: { name: entity.device.name },
33
+ native: entity.device,
34
+ });
35
+ const channel = entity.entity_id.substring(0, entity.entity_id.indexOf('.'));
36
+ await this.adapter.extendObject(entityInfo?.channel.id, {
37
+ type: 'channel',
38
+ common: { name: 'Channel of Entity' },
39
+ native: {},
40
+ });
41
+ let unique_id = entity.unique_id;
42
+ if (entity.unique_id.startsWith(channel)) {
43
+ unique_id = unique_id.substring(channel.length + 1, unique_id.length);
44
+ }
45
+ unique_id.replace('.', '_');
46
+ await this.adapter.extendObject(entityInfo?.state.id, {
47
+ type: 'state',
48
+ common: {
49
+ name: entityInfo?.state.name,
50
+ type: entityInfo?.state.type,
51
+ role: entityInfo?.state.role,
52
+ read: entityInfo?.state.read,
53
+ write: entityInfo?.state.write,
54
+ unit: entityInfo?.state.unit,
55
+ },
56
+ native: { entity: entity, entityInfo: entityInfo },
57
+ });
58
+ let state = entity.state;
59
+ if (entityInfo?.state.type === 'boolean') {
60
+ state = entity.state === 'on';
61
+ } else if (entityInfo?.state.type === 'number') {
62
+ state = Number(entity.state);
63
+ }
64
+ await this.adapter.setState(entityInfo?.state.id, state, true);
65
+ }
66
+ }
67
+ // Periodic discovery
68
+ if (message.discovery) {
69
+ const id = `${this.basefolder}.discoveredEntities`;
70
+ await this.adapter.extendObject(id, {
71
+ type: 'state',
72
+ common: {
73
+ name: 'Discovered Entities',
74
+ type: 'string',
75
+ role: 'json',
76
+ read: true,
77
+ write: false,
78
+ def: '',
79
+ },
80
+ native: {},
81
+ });
82
+
83
+ // Read current data
84
+ const discoveredEntities = await this.adapter.getStateAsync(id);
85
+ const checkDevices = {};
86
+ if (discoveredEntities.val) {
87
+ const entities = JSON.parse(discoveredEntities.val);
88
+ for (const entityId of Object.keys(entities)) {
89
+ if (!Object.keys(message.entities).includes(entityId)) {
90
+ const entityInfo = this.generateStructure(entities[entityId]);
91
+ await this.adapter.delObjectAsync(entityInfo?.state.id);
92
+ checkDevices[entityInfo?.device.id] = {};
93
+ if (entityInfo?.channel.id) {
94
+ checkDevices[entityInfo?.device.id][entityInfo?.channel.id] = {};
95
+ }
96
+ }
97
+ }
98
+
99
+ // Check for delete channels and devices
100
+ for (const deviceId of Object.keys(checkDevices)) {
101
+ let foundStateInAnyChannel = false;
102
+
103
+ // Check channel
104
+ for (const channelId of Object.keys(checkDevices[deviceId])) {
105
+ const channelParams = {
106
+ startkey: `${channelId}.`,
107
+ endkey: `${channelId}.\u9999`,
108
+ };
109
+
110
+ const channelStates = await this.adapter.getObjectViewAsync(
111
+ 'system',
112
+ 'state',
113
+ channelParams,
114
+ );
115
+
116
+ if (channelStates.rows.length > 0) {
117
+ // State found
118
+ foundStateInAnyChannel = true;
119
+ continue;
120
+ }
121
+
122
+ // No state
123
+ await this.adapter.delObjectAsync(channelId, { recursive: true });
124
+ this.adapter.log.debug(`Deleted empty channel: ${channelId}`);
125
+ }
126
+
127
+ // Check device
128
+ if (!foundStateInAnyChannel) {
129
+ const deviceParams = {
130
+ startkey: `${deviceId}.`,
131
+ endkey: `${deviceId}.\u9999`,
132
+ };
133
+
134
+ const deviceStates = await this.adapter.getObjectViewAsync('system', 'state', deviceParams);
135
+
136
+ if (deviceStates.rows.length === 0) {
137
+ await this.adapter.delObjectAsync(deviceId, { recursive: true });
138
+ this.adapter.log.debug(`Deleted empty device: ${deviceId}`);
139
+ }
140
+ }
141
+ }
142
+ }
143
+ await this.adapter.setState(id, JSON.stringify(message.entities), true);
144
+ }
145
+ } catch (error) {
146
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * @param entity entity wich contains the desired informations
152
+ */
153
+ generateStructure(entity) {
154
+ const activeFunction = 'bridgeDeviceHandler.js - generateStructure';
155
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
156
+ try {
157
+ if (!entity || !entity.entity_id) {
158
+ return null;
159
+ }
160
+
161
+ const [domain] = entity.entity_id.split('.');
162
+
163
+ const stateId = this.buildSafeStateId(entity);
164
+ if (!stateId) {
165
+ return null;
166
+ }
167
+ const type = this.detectType(entity);
168
+ const assign = this.detectAssign(entity);
169
+
170
+ const device = {
171
+ id: `${this.basefolder}.${entity.device?.id ?? 'unknown_device'}`,
172
+ name: entity.device?.name || 'Unknown Device',
173
+ manufacturer: entity.device?.manufacturer || '',
174
+ model: entity.device?.model || '',
175
+ };
176
+
177
+ const channel = {
178
+ id: `${this.basefolder}.${entity.device?.id}.${domain}`,
179
+ name: domain,
180
+ };
181
+
182
+ const state = {
183
+ id: `${this.basefolder}.${entity.device?.id}.${domain}.${stateId}`,
184
+ name: entity.friendly_name || stateId,
185
+ type: type,
186
+ role: this.detectRole(entity, domain, type),
187
+ unit: entity.unit_of_measurement || undefined,
188
+ read: true,
189
+ write: this.isWritable(domain),
190
+ };
191
+ if (assign) {
192
+ state.assign = assign;
193
+ }
194
+
195
+ const meta = {
196
+ entity_id: entity.entity_id,
197
+ unique_id: entity.unique_id,
198
+ device_class: entity.device_class,
199
+ state_class: entity.state_class,
200
+ };
201
+ return {
202
+ device: device,
203
+ channel: channel,
204
+ state: state,
205
+ meta: meta,
206
+ };
207
+ } catch (error) {
208
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * @param entity entity wich contains the deired informations
214
+ */
215
+ detectType(entity) {
216
+ const activeFunction = 'bridgeDeviceHandler.js - detectType';
217
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
218
+ try {
219
+ const s = entity.state;
220
+
221
+ if (['on', 'off', 'true', 'false'].includes(s)) {
222
+ return 'boolean';
223
+ }
224
+ if (!isNaN(s) && s !== '') {
225
+ return 'number';
226
+ }
227
+ return 'string';
228
+ } catch (error) {
229
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
230
+ }
231
+ }
232
+
233
+ /**
234
+ * @param entity entity wich contains the deired informations
235
+ */
236
+ detectAssign(entity) {
237
+ const activeFunction = 'bridgeDeviceHandler.js - detectAssign';
238
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
239
+ try {
240
+ const s = entity.state;
241
+ if (['on', 'off'].includes(s)) {
242
+ return { true: 'on', false: 'off' };
243
+ }
244
+ if (['true', 'false'].includes(s)) {
245
+ return { true: 'true', false: 'false' };
246
+ }
247
+ return null;
248
+ } catch (error) {
249
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
250
+ }
251
+ }
252
+
253
+ /**
254
+ * @param domain domain, wich contains the type of entity
255
+ */
256
+ isWritable(domain) {
257
+ const activeFunction = 'bridgeDeviceHandler.js - isWritable';
258
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
259
+ try {
260
+ return [
261
+ 'switch',
262
+ 'light',
263
+ 'input_boolean',
264
+ 'input_number',
265
+ 'input_select',
266
+ 'climate',
267
+ 'cover',
268
+ 'lock',
269
+ ].includes(domain);
270
+ } catch (error) {
271
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
272
+ }
273
+ }
274
+
275
+ /**
276
+ * @param entity entity wich contains the desired informations
277
+ * @param domain domain wich contains the type of the entity
278
+ * @param type type wich contains the type of ioBroker state
279
+ */
280
+ detectRole(entity, domain, type) {
281
+ const activeFunction = 'bridgeDeviceHandler.js - detectRole';
282
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
283
+ try {
284
+ if (domain === 'switch') {
285
+ return 'switch';
286
+ }
287
+ if (domain === 'light') {
288
+ return 'light';
289
+ }
290
+ if (domain === 'binary_sensor') {
291
+ return 'indicator';
292
+ }
293
+
294
+ if (entity.device_class) {
295
+ const map = {
296
+ temperature: 'value.temperature',
297
+ humidity: 'value.humidity',
298
+ power: 'value.power',
299
+ energy: 'value.energy',
300
+ window: 'sensor.window',
301
+ door: 'sensor.door',
302
+ };
303
+ if (map[entity.device_class]) {
304
+ return map[entity.device_class];
305
+ }
306
+ }
307
+
308
+ if (type === 'number') {
309
+ return 'value';
310
+ }
311
+ if (type === 'boolean') {
312
+ return 'indicator';
313
+ }
314
+ return 'state';
315
+ } catch (error) {
316
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
317
+ }
318
+ }
319
+
320
+ /**
321
+ * @param entity entity wich contains the deired informations
322
+ */
323
+ buildSafeStateId(entity) {
324
+ const activeFunction = 'bridgeDeviceHandler.js - buildSafeStateId';
325
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
326
+ try {
327
+ // 1. Basis: unique_id oder entity_id
328
+ const baseId = entity.unique_id || entity.entity_id;
329
+
330
+ if (!baseId) {
331
+ return null;
332
+ }
333
+
334
+ // if eg. switch in front => remove
335
+ const parts = baseId.split('.');
336
+ const raw =
337
+ parts.length > 1 && parts[0] === entity.entity_id?.split('.')[0] ? parts.slice(1).join('.') : baseId;
338
+
339
+ // 3. remove ".
340
+ return raw.replace(/\./g, '_');
341
+ } catch (error) {
342
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
343
+ }
344
+ }
345
+
346
+ /************************************************************************************
347
+ *
348
+ ************************************************************************************/
349
+
350
+ /**
351
+ * @param id id wich is to send
352
+ * @param state state of the id
353
+ */
354
+ async sendData(id, state) {
355
+ const activeFunction = 'bridgeDeviceHandler.js - sendData';
356
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
357
+ try {
358
+ const idObject = await this.adapter.getObjectAsync(id);
359
+ const sendInfo = {};
360
+ if (idObject.native.entityInfo.state.assign) {
361
+ sendInfo[idObject.native.entity.entity_id] = idObject.native.entityInfo.state.assign[state.val];
362
+ } else {
363
+ sendInfo[idObject.native.entity.entity_id] = state.val;
364
+ }
365
+ await this.adapter.bridge.publishId(`${this.adapter.namespace}.bridge.dataFromIob`, sendInfo, {});
366
+ await this.adapter.setState(`${this.adapter.namespace}.bridge.dataFromIob`, JSON.stringify(sendInfo), true);
367
+ } catch (error) {
368
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
369
+ }
370
+ }
371
+ }
372
+
373
+ module.exports = bridgeDeviceHandlerClass;
package/main.js CHANGED
@@ -876,6 +876,9 @@ class Lorawan extends utils.Adapter {
876
876
  await this.setState(id, state.val, true);
877
877
  await this.bridge.publishId(id, state.val, {});
878
878
  }
879
+ } else if (id.startsWith(`${this.namespace}.bridge.devices.`)) {
880
+ await this.bridge?.bridgeDeviceHandler.sendData(id, state);
881
+ await this.setState(id, state.val, true);
879
882
  }
880
883
  } else {
881
884
  // Query for 0_userdata or alias => states also publish with ack = false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.lorawan",
3
- "version": "1.18.63",
3
+ "version": "1.19.0",
4
4
  "description": "converts the desired lora gateway data to a ioBroker structure",
5
5
  "author": {
6
6
  "name": "BenAhrdt",