iobroker.lorawan 1.14.7 → 1.14.8

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.14.8 (2025-09-13)
27
+ * (BenAhrdt) Add general, offline and online Notifications to the discovered devices
28
+ * (BenAhrdt) Add general Notifications to device called namespace of the Adapter
29
+
26
30
  ### 1.14.7 (2025-09-12)
27
31
  * (BenAhrdt) Add Folders for Target Temperature in Heating Events
28
32
  * (BenAhrdt) correct unit handling & Max valueCount in Tab
@@ -784,6 +784,22 @@
784
784
  "lg": 2,
785
785
  "xl": 2
786
786
  },
787
+ "BridgenotificationActivation":{
788
+ "type": "select",
789
+ "label": "notificationActivation",
790
+ "hidden": "data.BridgeType === 'off'",
791
+ "tooltip": "notificationActivationTooltip",
792
+ "options": [
793
+ {"label":"notification","value":"notification"},
794
+ {"label":"no notification","value":"no notification"}
795
+ ],
796
+ "default": "notification",
797
+ "xs": 12,
798
+ "sm": 3,
799
+ "md": 3,
800
+ "lg": 3,
801
+ "xl": 3
802
+ },
787
803
  "_BridgeServersettingsHeader": {
788
804
  "newLine": true,
789
805
  "type": "header",
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "lorawan",
4
- "version": "1.14.7",
4
+ "version": "1.14.8",
5
5
  "news": {
6
+ "1.14.8": {
7
+ "en": "Add general, offline and online notifications to the discovered devices\nAdd general notifications to device called namespace of the adapter",
8
+ "de": "Allgemeine, Offline- und Online-Benachrichtigungen zu den erkannten Geräten hinzufügen\nAllgemeine Benachrichtigungen zum Gerät namens Namespace des Adapters hinzufügen",
9
+ "ru": "Добавлены общие, офлайн и онлайн уведомления для обнаруженных устройств\nДобавлены общие уведомления к устройству с именем namespace адаптера",
10
+ "pt": "Adicionar notificações gerais, offline e online aos dispositivos descobertos\nAdicionar notificações gerais ao dispositivo chamado namespace do adaptador",
11
+ "nl": "Algemene, offline en online meldingen toevoegen aan de gedetecteerde apparaten\nAlgemene meldingen toevoegen aan apparaat genaamd namespace van de adapter",
12
+ "fr": "Ajouter des notifications générales, hors ligne et en ligne aux périphériques découverts\nAjouter des notifications générales à l'appareil appelé espace de noms de l'adaptateur",
13
+ "it": "Aggiungere notifiche generali, offline e online ai dispositivi scoperti\nAggiungere notifiche generali al dispositivo chiamato namespace dell'adattatore",
14
+ "es": "Agregar notificaciones generales, fuera de línea y en línea a los dispositivos descubiertos\nAgregar notificaciones generales al dispositivo llamado namespace del adaptador",
15
+ "pl": "Dodano ogólne powiadomienia offline i online do wykrytych urządzeń\nDodano ogólne powiadomienia do urządzenia o nazwie namespace adaptera",
16
+ "uk": "Додано загальні, офлайн та онлайн сповіщення до виявлених пристроїв\nДодано загальні сповіщення до пристрою під назвою namespace адаптера",
17
+ "zh-cn": "为发现的设备添加常规、离线和在线通知\n为名为适配器命名空间的设备添加常规通知"
18
+ },
6
19
  "1.14.7": {
7
20
  "en": "Add folders for target temperature in heating events\nCorrect unit handling and max value count in tab",
8
21
  "de": "Ordner für Solltemperatur in Heizungsevents hinzufügen\nKorrekte Einheitenbehandlung und maximale Wertanzahl im Tab",
@@ -80,19 +93,6 @@
80
93
  "pl": "Poprawka błędu climate current_temperature topic",
81
94
  "uk": "Виправлення помилки в climate current_temperature topic",
82
95
  "zh-cn": "修复 climate current_temperature topic 的错误"
83
- },
84
- "1.14.1": {
85
- "en": "Bugfix unique_id of climate entities",
86
- "de": "Bugfix unique_id von Climate-Entities",
87
- "ru": "Исправлена ошибка unique_id для climate entities",
88
- "pt": "Correção de bug no unique_id de entidades de clima",
89
- "nl": "Bugfix unique_id van climate entities",
90
- "fr": "Correction de bug du unique_id des entités de climat",
91
- "it": "Correzione bug unique_id delle entità climate",
92
- "es": "Corrección de error en unique_id de entidades de clima",
93
- "pl": "Poprawka błędu unique_id dla encji klimatycznych",
94
- "uk": "Виправлення помилки unique_id для сутностей клімату",
95
- "zh-cn": "修复气候实体的 unique_id 错误"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -358,6 +358,7 @@
358
358
  "BridgeType": "off",
359
359
  "EnableRefresh": true,
360
360
  "RefreshDiscoveryCronJob": "0 * * * *",
361
+ "BridgenotificationActivation": "notification",
361
362
  "DeviceIdentifiers": [
362
363
  {
363
364
  "DeviceIdentifier": "usedApplicationName"
@@ -25,6 +25,7 @@ class bridgeClass {
25
25
  this.SubscribedTopics = {};
26
26
  this.PublishedIds = {};
27
27
  this.VitualIds = {};
28
+ this.Notifications = {};
28
29
  this.BridgeDiscoveryPrefix = {
29
30
  HA: 'homeassistant/',
30
31
  };
@@ -34,8 +35,20 @@ class bridgeClass {
34
35
  this.EndingState = '/state';
35
36
  this.EndingVirtualClimate = '.virtual_climate';
36
37
  this.EndingVirtualMode = '.virtual_mode';
38
+ this.NotificationId = '.notification';
39
+ this.GeneralId = '.general';
40
+ this.OfflineId = '.offline';
41
+ this.OnlineId = '.online';
42
+ this.EndingNotification = '.notification';
37
43
  this.ClimateEntityType = 'climate';
44
+ this.NotificationEntityType = 'device_automation';
38
45
  this.MaxValueCount = 5;
46
+ this.Words = {
47
+ notification: 'notification',
48
+ general: 'general',
49
+ offline: 'offline',
50
+ online: 'online',
51
+ };
39
52
 
40
53
  // Timeoutput always like german view
41
54
  this.Timeoutput = {
@@ -247,6 +260,147 @@ class bridgeClass {
247
260
  }
248
261
  }
249
262
 
263
+ /*********************************************************************
264
+ * *************** Discover Device Notifications *********************
265
+ * ******************************************************************/
266
+
267
+ /**
268
+ * @param changeInfo changeInfo for the device
269
+ */
270
+ async discoverDeviceNotifications(changeInfo) {
271
+ const activeFunction = 'discoverDeviceNotifications';
272
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
273
+ try {
274
+ const notificationId = `${changeInfo.objectStartDirectory}${this.NotificationId}${this.GeneralId}`;
275
+ if (!this.Notifications[notificationId]) {
276
+ const deviceIdentifier = this.getDeviceIdentifier(changeInfo, this.adapter.config.DeviceIdentifiers);
277
+ const normalizedDeviceIdentifier = this.normalizeString(deviceIdentifier);
278
+ const general = {
279
+ topic: `${this.bridgeMqttClient.BridgePrefix}${normalizedDeviceIdentifier}/${this.Words.notification}_${this.Words.general}${this.EndingState}`.toLowerCase(),
280
+ notificationId: notificationId,
281
+ };
282
+ let discoveryobject = this.getNotificationDiscoveryObject(deviceIdentifier, this.Words.general);
283
+ this.Notifications[general.notificationId] = {};
284
+ if (!this.PublishedIds[general.notificationId]) {
285
+ this.PublishedIds[general.notificationId] = { discovery: [] };
286
+ }
287
+ this.PublishedIds[general.notificationId].discovery.push({
288
+ topic: discoveryobject?.topic,
289
+ payload: structuredClone(discoveryobject?.payload),
290
+ });
291
+ this.PublishedIds[general.notificationId].state_topic = discoveryobject?.payload.topic;
292
+ await this.publishDiscovery(general.notificationId, {
293
+ topic: discoveryobject?.topic,
294
+ payload: structuredClone(discoveryobject?.payload),
295
+ });
296
+
297
+ // offline
298
+ const offline = {
299
+ topic: `${this.bridgeMqttClient.BridgePrefix}${normalizedDeviceIdentifier}/${this.Words.notification}_${this.Words.offline}${this.EndingState}`.toLowerCase(),
300
+ notificationId: `${changeInfo.objectStartDirectory}${this.NotificationId}${this.OfflineId}`,
301
+ };
302
+ discoveryobject = this.getNotificationDiscoveryObject(deviceIdentifier, this.Words.offline);
303
+ this.Notifications[offline.notificationId] = {};
304
+ if (!this.PublishedIds[offline.notificationId]) {
305
+ this.PublishedIds[offline.notificationId] = { discovery: [] };
306
+ }
307
+ this.PublishedIds[offline.notificationId].discovery.push({
308
+ topic: discoveryobject?.topic,
309
+ payload: structuredClone(discoveryobject?.payload),
310
+ });
311
+ this.PublishedIds[offline.notificationId].state_topic = discoveryobject?.payload.topic;
312
+ await this.publishDiscovery(offline.notificationId, {
313
+ topic: discoveryobject?.topic,
314
+ payload: structuredClone(discoveryobject?.payload),
315
+ });
316
+
317
+ // online
318
+ const online = {
319
+ topic: `${this.bridgeMqttClient.BridgePrefix}${normalizedDeviceIdentifier}/${this.Words.notification}_${this.Words.online}${this.EndingState}`.toLowerCase(),
320
+ notificationId: `${changeInfo.objectStartDirectory}${this.NotificationId}${this.OnlineId}`,
321
+ };
322
+ discoveryobject = this.getNotificationDiscoveryObject(deviceIdentifier, this.Words.online);
323
+ this.Notifications[online.notificationId] = {};
324
+ if (!this.PublishedIds[online.notificationId]) {
325
+ this.PublishedIds[online.notificationId] = { discovery: [] };
326
+ }
327
+ this.PublishedIds[online.notificationId].discovery.push({
328
+ topic: discoveryobject?.topic,
329
+ payload: structuredClone(discoveryobject?.payload),
330
+ });
331
+ this.PublishedIds[online.notificationId].state_topic = discoveryobject?.payload.topic;
332
+ await this.publishDiscovery(online.notificationId, {
333
+ topic: discoveryobject?.topic,
334
+ payload: structuredClone(discoveryobject?.payload),
335
+ });
336
+ }
337
+ } catch (error) {
338
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
339
+ }
340
+ }
341
+
342
+ /*********************************************************************
343
+ * ******************** Discover Notification ************************
344
+ * ******************************************************************/
345
+
346
+ /**
347
+ * Discover Notifications to Bridge
348
+ */
349
+ async discoverGeneralNotification() {
350
+ const activeFunction = 'discoverGeneralNotification';
351
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
352
+ try {
353
+ const notificationId = `${this.Words.notification}${this.GeneralId}`;
354
+ if (!this.Notifications[notificationId]) {
355
+ const discoveryobject = this.getNotificationDiscoveryObject(this.adapter.namespace, this.Words.general);
356
+ this.Notifications[notificationId] = {};
357
+ if (!this.PublishedIds[notificationId]) {
358
+ this.PublishedIds[notificationId] = { discovery: [] };
359
+ }
360
+ this.PublishedIds[notificationId].discovery.push({
361
+ topic: discoveryobject?.topic,
362
+ payload: structuredClone(discoveryobject?.payload),
363
+ });
364
+ this.PublishedIds[notificationId].state_topic = discoveryobject?.payload.topic;
365
+ await this.publishDiscovery(notificationId, {
366
+ topic: discoveryobject?.topic,
367
+ payload: structuredClone(discoveryobject?.payload),
368
+ });
369
+ }
370
+ } catch (error) {
371
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
372
+ }
373
+ }
374
+
375
+ /*********************************************************************
376
+ * **************** Get Notification Discovery Object ****************
377
+ * ******************************************************************/
378
+
379
+ /**
380
+ * @param deviceIdentifier deviceidentifiere for the desired device
381
+ * @param notificationType notificationtype for the discoveryobject
382
+ */
383
+ getNotificationDiscoveryObject(deviceIdentifier, notificationType) {
384
+ const activeFunction = 'getNotificationDiscoveryObject';
385
+ this.adapter.log.debug(`Function ${activeFunction} started.`);
386
+ try {
387
+ const normalizedDeviceIdentifier = this.normalizeString(deviceIdentifier);
388
+ const discoveryobject = {
389
+ topic: `${this.BridgeDiscoveryPrefix[this.adapter.config.BridgeType]}${this.NotificationEntityType}/${normalizedDeviceIdentifier}/${this.Words.notification}_${notificationType}/config`.toLowerCase(),
390
+ payload: {
391
+ automation_type: 'trigger',
392
+ topic: `${this.bridgeMqttClient.BridgePrefix}${normalizedDeviceIdentifier}/${this.Words.notification}_${notificationType}${this.EndingState}`.toLowerCase(),
393
+ type: 'notification',
394
+ subtype: notificationType,
395
+ device: { identifiers: [normalizedDeviceIdentifier.toLowerCase()], name: deviceIdentifier },
396
+ },
397
+ };
398
+ return discoveryobject;
399
+ } catch (error) {
400
+ this.adapter.log.error(`error at ${activeFunction}: ${error}`);
401
+ }
402
+ }
403
+
250
404
  /*********************************************************************
251
405
  * ********************** Discover Climate ***************************
252
406
  * ******************************************************************/
@@ -271,9 +425,9 @@ class bridgeClass {
271
425
  target.changeInfo,
272
426
  this.adapter.config.DeviceIdentifiers,
273
427
  );
274
- target.normalizedDeficeIdentifier = await this.normalizeString(target.DeviceIdentifier);
428
+ target.normalizedDeficeIdentifier = this.normalizeString(target.DeviceIdentifier);
275
429
  target.Topic =
276
- `${this.bridgeMqttClient.BridgePrefix}${target.normalizedDeficeIdentifier}/${target.changeInfo.changedState}`.toLocaleLowerCase();
430
+ `${this.bridgeMqttClient.BridgePrefix}${target.normalizedDeficeIdentifier}/${target.changeInfo.changedState}`.toLowerCase();
277
431
 
278
432
  //Min und Max holen
279
433
  const targetObject = await this.adapter.getObjectAsync(config.climateIds.target);
@@ -302,13 +456,13 @@ class bridgeClass {
302
456
  mode.changeInfo,
303
457
  this.adapter.config.DeviceIdentifiers,
304
458
  );
305
- mode.normalizedDeviceIdentifier = await this.normalizeString(mode.DeviceIdentifier);
459
+ mode.normalizedDeviceIdentifier = this.normalizeString(mode.DeviceIdentifier);
306
460
  mode.Topic =
307
461
  `${this.bridgeMqttClient.BridgePrefix}${mode.normalizedDeviceIdentifier}/${mode.changeInfo.changedState}`.toLowerCase();
308
462
 
309
- const normalizedClimateName = await this.normalizeString(config.ClimateName);
463
+ const normalizedClimateName = this.normalizeString(config.ClimateName);
310
464
  const DiscoveryTopic =
311
- `${this.BridgeDiscoveryPrefix[this.adapter.config.BridgeType]}${this.ClimateEntityType}/${target.normalizedDeficeIdentifier}/${normalizedClimateName}/config`.toLocaleLowerCase();
465
+ `${this.BridgeDiscoveryPrefix[this.adapter.config.BridgeType]}${this.ClimateEntityType}/${target.normalizedDeficeIdentifier}/${normalizedClimateName}/config`.toLowerCase();
312
466
  const indexLastDotTarget = config.climateIds.target.lastIndexOf('.');
313
467
  const Id = config.climateIds.target.substring(0, indexLastDotTarget) + this.EndingVirtualClimate;
314
468
  const DiscoveryPayload = {
@@ -611,6 +765,9 @@ class bridgeClass {
611
765
  usedDeviceId: changeInfo.usedDeviceId,
612
766
  },
613
767
  });
768
+ if (this.adapter.config.BridgenotificationActivation === 'notification') {
769
+ await this.discoverDeviceNotifications(changeInfo);
770
+ }
614
771
  }
615
772
  }
616
773
 
@@ -686,6 +843,9 @@ class bridgeClass {
686
843
  usedDeviceId: changeInfo.usedDeviceId,
687
844
  },
688
845
  });
846
+ if (this.adapter.config.BridgenotificationActivation === 'notification') {
847
+ await this.discoverDeviceNotifications(changeInfo);
848
+ }
689
849
  }
690
850
  }
691
851
  }
@@ -721,14 +881,14 @@ class bridgeClass {
721
881
  }
722
882
  const StateName = changeInfo.id.substring(indexOfStatebegin, changeInfo.id.length);
723
883
  const normalizedStateName = this.normalizeString(StateName);
724
- const DeviceIdentifier = await this.getDeviceIdentifier(changeInfo, this.adapter.config.DeviceIdentifiers);
725
- const normalizedDeviceIdentifier = await this.normalizeString(DeviceIdentifier);
884
+ const DeviceIdentifier = this.getDeviceIdentifier(changeInfo, this.adapter.config.DeviceIdentifiers);
885
+ const normalizedDeviceIdentifier = this.normalizeString(DeviceIdentifier);
726
886
  const Topic =
727
- `${this.bridgeMqttClient.BridgePrefix}${normalizedDeviceIdentifier}/${normalizedStateName}`.toLocaleLowerCase();
887
+ `${this.bridgeMqttClient.BridgePrefix}${normalizedDeviceIdentifier}/${normalizedStateName}`.toLowerCase();
728
888
  const EntityType = await this.getEntityType(options);
729
889
  const AdditionalAttributes = await this.getStateAttributes(options.common, EntityType);
730
890
  const DiscoveryTopic =
731
- `${this.BridgeDiscoveryPrefix[this.adapter.config.BridgeType]}${EntityType}/${normalizedDeviceIdentifier}/${normalizedStateName}/config`.toLocaleLowerCase();
891
+ `${this.BridgeDiscoveryPrefix[this.adapter.config.BridgeType]}${EntityType}/${normalizedDeviceIdentifier}/${normalizedStateName}/config`.toLowerCase();
732
892
  const DiscoveryPayload = {
733
893
  name: StateName,
734
894
  unique_id: `${normalizedDeviceIdentifier}_${normalizedStateName}`.toLowerCase(),
@@ -1156,6 +1316,9 @@ class bridgeClass {
1156
1316
  }
1157
1317
  }
1158
1318
  await this.discoverClimate();
1319
+ if (this.adapter.config.BridgenotificationActivation === 'notification') {
1320
+ await this.discoverGeneralNotification();
1321
+ }
1159
1322
  } catch (error) {
1160
1323
  this.adapter.log.error(`error at ${activeFunction}: ${error}`);
1161
1324
  }
@@ -18,7 +18,7 @@ class bridgeMqttClientClass {
18
18
  });
19
19
 
20
20
  // Prefix for publish and subscrip
21
- this.BridgePrefix = 'lorawan/';
21
+ this.BridgePrefix = `${this.adapter.namespace}/`.replace(/\./g, '_');
22
22
 
23
23
  // Variables for correct connection (disconnection) notification / logging
24
24
  this.internalConnectionstate = false;
@@ -42,6 +42,12 @@ class bridgeMqttClientClass {
42
42
 
43
43
  await this.adapter.bridge.checkAllStatesForBridgeWork({ forceDiscovery: true });
44
44
  this.adapter.bridge.createScheduleJobs();
45
+ if (this.adapter.config.BridgenotificationActivation === 'notification' && this.adapter.bridge) {
46
+ const devicename = `${this.adapter.bridge.Words.notification}${this.adapter.bridge.GeneralId}`;
47
+ if (this.adapter.bridge.Notifications[devicename]) {
48
+ this.adapter.bridge.publishId(devicename, `Connection to Bridge is active.`);
49
+ }
50
+ }
45
51
  });
46
52
 
47
53
  this.client.on('disconnect', () => {
@@ -296,11 +296,18 @@ class directorieshandlerClass {
296
296
  // create changeinfo and register notification
297
297
  const changeInfo = await this.adapter.getChangeInfo(objectId);
298
298
  if (changeInfo) {
299
+ const message = `The LoRaWAN device ${changeInfo.usedDeviceId} in the application ${changeInfo.usedApplicationName} is back online`;
299
300
  this.adapter.registerNotification(
300
301
  'lorawan',
301
302
  'Lorawan device back online',
302
- `The LoRaWAN device ${changeInfo.usedDeviceId} in the application ${changeInfo.usedApplicationName} is back online`,
303
+ message,
303
304
  );
305
+ if (this.adapter.bridge) {
306
+ const notificationId = `${changeInfo.applicationId}.devices.${changeInfo.deviceEUI}${this.adapter.bridge.NotificationId}${this.adapter.bridge.OnlineId}`;
307
+ if (this.adapter.bridge.Notifications[notificationId]) {
308
+ this.adapter.bridge.publishId(notificationId, message);
309
+ }
310
+ }
304
311
  }
305
312
  }
306
313
  }
@@ -80,11 +80,17 @@ class messagehandlerClass {
80
80
  // create changeinfo and register notification
81
81
  const changeInfo = await this.adapter.getChangeInfo(adapterObject._id);
82
82
  if (changeInfo) {
83
- this.adapter.registerNotification(
84
- 'lorawan',
85
- 'LoRaWAN device offline',
86
- `The LoRaWAN device ${changeInfo.usedDeviceId} in the application ${changeInfo.usedApplicationName} is offline`,
87
- );
83
+ const message = `The LoRaWAN device ${changeInfo.usedDeviceId} in the application ${changeInfo.usedApplicationName} is offline`;
84
+ this.adapter.registerNotification('lorawan', 'LoRaWAN device offline', message);
85
+ if (
86
+ this.adapter.config.BridgenotificationActivation === 'notification' &&
87
+ this.adapter.bridge
88
+ ) {
89
+ const notificationId = `${changeInfo.applicationId}.devices.${changeInfo.deviceEUI}${this.adapter.bridge.NotificationId}${this.adapter.bridge.OfflineId}`;
90
+ if (this.adapter.bridge.Notifications[notificationId]) {
91
+ this.adapter.bridge.publishId(notificationId, message);
92
+ }
93
+ }
88
94
  }
89
95
  }
90
96
  }
package/main.js CHANGED
@@ -382,6 +382,14 @@ class Lorawan extends utils.Adapter {
382
382
  */
383
383
  onUnload(callback) {
384
384
  try {
385
+ // Ausgabe der Nachrichtg, dass der Adapter beendet wird
386
+ if (this.config.BridgenotificationActivation === 'notification' && this.bridge) {
387
+ const devicename = `${this.bridge?.Words.notification}${this.bridge?.GeneralId}`;
388
+ if (this.bridge?.Notifications[devicename]) {
389
+ this.bridge?.publishId(devicename, `Connection to Bridge will be closed.`);
390
+ }
391
+ }
392
+
385
393
  // clear timeout (for simulation)
386
394
  if (this.simulation.timeout) {
387
395
  this.clearTimeout(this.simulation.timeout);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.lorawan",
3
- "version": "1.14.7",
3
+ "version": "1.14.8",
4
4
  "description": "converts the desired lora gateway data to a ioBroker structure",
5
5
  "author": {
6
6
  "name": "BenAhrdt",