iobroker.smartfriends 1.3.5 → 1.3.6-alpha.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
@@ -33,6 +33,11 @@ The adapter establishes a direct connection to the gateway to control and query
33
33
  Placeholder for the next version (at the beginning of the line):
34
34
  ### __WORK IN PROGRESS__
35
35
  -->
36
+ ### 1.3.6-alpha.1 (2026-03-02)
37
+
38
+ - (Black-Thunder) Connection and reconnection logic to the gateway was refactored
39
+ - (Black-Thunder) Adapter requires admin version >=7.6.20 now
40
+
36
41
  ### 1.3.5 (2026-02-24)
37
42
 
38
43
  - (Black-Thunder) Additional logging when connection parameters are incorrect was added
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "smartfriends",
4
- "version": "1.3.5",
4
+ "version": "1.3.6-alpha.1",
5
5
  "news": {
6
+ "1.3.6-alpha.1": {
7
+ "en": "Connection and reconnection logic to the gateway was refactored\nAdapter requires admin version >=7.6.20 now",
8
+ "de": "Verbindungs- und Wiederverbindungslogik zum Gateway wurde überarbeitet\nAdapter erfordert nun Admin-Version >=7.6.20",
9
+ "ru": "Логика подключения и переподключения к шлюзу была рефакторирована\nАдаптер требует админ версии >=7.6.20",
10
+ "pt": "A lógica de conexão e reconexão ao gateway foi refactorada\nAdaptador requer versão de administrador >= 7.6.20 agora",
11
+ "nl": "Verbinding en herverbinding logica met de gateway werd gerefactored\nAdapter vereist admin-versie < 7.6.20 nu",
12
+ "fr": "La logique de connexion et de reconnection à la passerelle a été remaniée\nAdaptateur nécessite une version admin >=7.6.20 maintenant",
13
+ "it": "La logica di connessione e di riconnessione al gateway è stata rifatto\nL'adattatore richiede la versione di amministratore >=7.6.20 ora",
14
+ "es": "La lógica de conexión y reconexión a la puerta de entrada fue refactorizada\nAdaptador requiere la versión de administración",
15
+ "pl": "Połączenie i ponowne podłączenie logiki do bramy zostało zrefakturowane\nAdapter wymaga wersji admin > = 7.6.20",
16
+ "uk": "З'єднання і відключення логіки до шлюзу було рефакторовано\nАдаптер вимагає адмін версії >=7.6.20 тепер",
17
+ "zh-cn": "连接和重新连接到网关的逻辑被重构\n适配器需要管理员版本 \\\\ 7.6.20 现在"
18
+ },
19
+ "1.3.6-alpha.0": {
20
+ "en": "Connection and reconnection logic to the gateway was refactored",
21
+ "de": "Verbindungs- und Wiederverbindungslogik zum Gateway wurde überarbeitet",
22
+ "ru": "Логика подключения и переподключения к шлюзу была рефакторирована",
23
+ "pt": "A lógica de conexão e reconexão ao gateway foi refactorada",
24
+ "nl": "Verbinding en herverbinding logica met de gateway werd gerefactored",
25
+ "fr": "La logique de connexion et de reconnection à la passerelle a été remaniée",
26
+ "it": "La logica di connessione e di riconnessione al gateway è stata rifatto",
27
+ "es": "La lógica de conexión y reconexión a la puerta de entrada fue refactorizada",
28
+ "pl": "Połączenie i ponowne podłączenie logiki do bramy zostało zrefakturowane",
29
+ "uk": "З'єднання і відключення логіки до шлюзу було рефакторовано",
30
+ "zh-cn": "连接和重新连接到网关的逻辑被重构"
31
+ },
6
32
  "1.3.5": {
7
33
  "en": "Additional logging when connection parameters are incorrect was added\nValidation of the adapter configuration in the GUI was added",
8
34
  "de": "Zusätzliche Protokollierung eingefügt, wenn Verbindungsparameter inkorrekt sind\nValidierung der Adapterkonfiguration in der GUI wurde hinzugefügt",
@@ -67,32 +93,6 @@
67
93
  "pl": "Zaktualizowano uzależnienia",
68
94
  "uk": "Залежність оновлено",
69
95
  "zh-cn": "附属关系得到更新"
70
- },
71
- "1.3.0": {
72
- "en": "Support for further device types was added\nUmlauts in device names are now correctly parsed",
73
- "de": "Unterstützung für weitere Gerätetypen wurde hinzugefügt\nUmlaute in Gerätenamen werden nun korrekt dargestellt",
74
- "ru": "Добавлена поддержка дополнительных типов устройств\nУмлауты в названиях устройств теперь правильно разобраны",
75
- "pt": "Foi adicionado suporte para outros tipos de dispositivos\nUmlauts em nomes de dispositivos agora são corretamente analisados",
76
- "nl": "Ondersteuning voor andere apparaattypes is toegevoegd\nUmlauts in apparaatnamen zijn nu correct ontleed",
77
- "fr": "Un soutien pour d'autres types d'appareils a été ajouté\nUmlauts dans les noms de périphériques sont maintenant correctement analysés",
78
- "it": "È stato aggiunto il supporto per ulteriori tipi di dispositivo\nI nomi dei dispositivi sono ora correttamente analizzati",
79
- "es": "Se agregó soporte para nuevos tipos de dispositivos\nUmlauts en los nombres de los dispositivos ahora se analizan correctamente",
80
- "pl": "Dodano wsparcie dla innych typów urządzeń\nKlauty w nazwach urządzeń są teraz poprawnie przefiltrowane",
81
- "uk": "Додана підтримка подальших типів пристроїв\nУмлаути в назвах пристроїв тепер правильно припаровані",
82
- "zh-cn": "添加了对更多设备类型的支持\n设备名称中的 Umlaut 现已正确解析"
83
- },
84
- "1.2.0": {
85
- "en": "Timeout for initial device request was increased\nDevices without defined device type are ignored\nRefactored device handling and added support for further device types",
86
- "de": "Timeout für erste Geräteanfrage wurde erhöht\nGeräte ohne definierten Gerätetyp werden ignoriert\nVerarbeitung der Geräte wurde überarbeitet und Unterstützung für weitere Gerätetypen hinzugefügt",
87
- "ru": "Время ожидания первоначального запроса устройства было увеличено\nУстройства без определенного типа устройства игнорируются\nРефакторированная обработка устройств и дополнительная поддержка для других типов устройств",
88
- "pt": "O tempo limite para a solicitação inicial do dispositivo foi aumentado\nOs dispositivos sem tipo de dispositivo definido são ignorados\nManipulação do dispositivo refatorizado e suporte adicional para outros tipos de dispositivo",
89
- "nl": "Tijdslimiet voor het eerste apparaatverzoek werd verhoogd\nApparaten zonder gedefinieerd apparaattype worden genegeerd\nRefactored device handling en toegevoegde ondersteuning voor andere apparaattypes",
90
- "fr": "Le délai pour la demande initiale d'appareil a été augmenté\nLes dispositifs sans type défini sont ignorés\nManipulation de l'appareil refactoré et support supplémentaire pour d'autres types d'appareil",
91
- "it": "Timeout per la richiesta iniziale del dispositivo è stato aumentato\nI dispositivi senza tipo di dispositivo definito vengono ignorati\nGestione del dispositivo e supporto aggiunto per ulteriori tipi di dispositivo",
92
- "es": "Se aumentó el tiempo para la solicitud inicial del dispositivo\nLos dispositivos sin tipo de dispositivo definido son ignorados\nManejo de dispositivo refactorizado y soporte añadido para nuevos tipos de dispositivos",
93
- "pl": "Zwiększono czas na początkowe żądanie urządzenia\nUrządzenia bez określonego typu urządzenia są ignorowane\nZmieniona obsługa urządzenia i dodana obsługa dla innych typów urządzeń",
94
- "uk": "Підвищено запит на початковий пристрій\nПристрої без визначеного типу пристрою ігноруються\nРефакторний пристрій обробки та додано підтримку для подальших типів пристроїв",
95
- "zh-cn": "初始设备请求的超时增加\n没有定义设备类型的设备会被忽略\n对设备类型进行重构处理并添加支持"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -154,7 +154,7 @@
154
154
  ],
155
155
  "globalDependencies": [
156
156
  {
157
- "admin": ">=7.6.17"
157
+ "admin": ">=7.6.20"
158
158
  }
159
159
  ],
160
160
  "tier": 2
@@ -30,12 +30,17 @@ class SchellenbergBridge {
30
30
  this.compatibilityConfigurationVersion = "0";
31
31
  this.languageTranslationVersion = "0";
32
32
  this.retryCounter = 0;
33
- this.stopRenewal = false;
34
33
  this.deviceManager = new DeviceManager(this.adapter);
35
34
  this.deviceDefinitions = [];
35
+ this.connectionStatus = commonDefines.ConnectionStatus.IDLE;
36
36
  }
37
37
 
38
38
  async Connect() {
39
+ if (this.connectionStatus === commonDefines.ConnectionStatus.CONNECTING) {
40
+ return;
41
+ }
42
+ this.connectionStatus = commonDefines.ConnectionStatus.CONNECTING;
43
+
39
44
  try {
40
45
  const socket = await SmartSocketFactory.default.createSocketAndLogin(
41
46
  this.adapter,
@@ -56,9 +61,11 @@ class SchellenbergBridge {
56
61
  throw new Error("Invalid socket or login response");
57
62
  }
58
63
 
64
+ this.retryCounter = 0;
65
+ this.connectionStatus = commonDefines.ConnectionStatus.CONNECTED;
59
66
  this.adapter.setAdapterConnectionState(true);
60
- this.adapter.log.info("Querying all devices...");
61
67
 
68
+ this.adapter.log.info("Connected successfully. Querying all devices...");
62
69
  const command = CommandFactory.default.createAllNewInfoCmd(
63
70
  this.lastTimestamp,
64
71
  this.compatibilityConfigurationVersion,
@@ -77,7 +84,10 @@ class SchellenbergBridge {
77
84
  } catch (err) {
78
85
  const msg = err?.responseMessage || err.message || JSON.stringify(err);
79
86
  this.adapter.log.error(`Connection failed: ${msg}`);
80
- this.handleDisconnect();
87
+
88
+ this.connectionStatus = commonDefines.ConnectionStatus.IDLE;
89
+
90
+ this.scheduleReconnect();
81
91
  }
82
92
  }
83
93
 
@@ -127,7 +137,7 @@ class SchellenbergBridge {
127
137
  }
128
138
 
129
139
  handleDeviceInfo(deviceInfo) {
130
- if (!deviceInfo || deviceInfo.deviceName || !deviceInfo.deviceDesignation) {
140
+ if (!deviceInfo || !deviceInfo.deviceName || !deviceInfo.deviceDesignation) {
131
141
  this.adapter.log.debug("Invalid device info received, ignoring.");
132
142
  return;
133
143
  }
@@ -228,18 +238,38 @@ class SchellenbergBridge {
228
238
  }
229
239
  }
230
240
 
231
- handleDisconnect() {
241
+ handleDisconnect(manual = false) {
242
+ if (
243
+ this.connectionStatus === commonDefines.ConnectionStatus.WAITING_RETRY ||
244
+ this.connectionStatus === commonDefines.ConnectionStatus.CONNECTING
245
+ ) {
246
+ return;
247
+ }
248
+
249
+ this.adapter.log.debug("handleDisconnect() was called. Rejecting all queued promises and stopping keep alive.");
232
250
  this.adapter.setAdapterConnectionState(false);
233
- this.adapter.log.debug("Handle Disconnect was called. Rejecting all queued promises and stopping keep alive.");
251
+ this.connectionStatus = commonDefines.ConnectionStatus.IDLE;
252
+
234
253
  this.promiseQueue.forEach(entry => {
235
- entry.reject("Socket disconnected");
254
+ entry.reject(new Error("Socket disconnected"));
236
255
  });
256
+ this.promiseQueue = [];
237
257
 
238
- this.stopRenewal = true;
239
258
  if (this.socket) {
240
259
  this.socket.stopKeepAlive();
241
260
  this.socket = null;
242
261
  }
262
+
263
+ if (this.reconnectTimer) {
264
+ this.adapter.clearTimeout(this.reconnectTimer);
265
+ this.reconnectTimer = null;
266
+ }
267
+
268
+ this.loginResponse = null;
269
+
270
+ if (!manual) {
271
+ this.scheduleReconnect();
272
+ }
243
273
  }
244
274
 
245
275
  handleMessage(data) {
@@ -273,7 +303,6 @@ class SchellenbergBridge {
273
303
  const responseHandlers = {
274
304
  [-1]: r => this.handleErrorResponse(r),
275
305
  [1]: r => {
276
- this.retryCounter = 0;
277
306
  this.resolveNextPromise(r);
278
307
  this.updateTimestamp(r);
279
308
  },
@@ -322,7 +351,7 @@ class SchellenbergBridge {
322
351
  "Connection was closed, because credentials were used to login on a different device or the official app.",
323
352
  );
324
353
 
325
- this.reconnect();
354
+ this.scheduleReconnect();
326
355
  break;
327
356
  }
328
357
  default:
@@ -360,22 +389,36 @@ class SchellenbergBridge {
360
389
 
361
390
  reconnectWithWarn(message) {
362
391
  this.adapter.log.warn(message);
363
- this.reconnect();
392
+ this.handleDisconnect();
364
393
  }
365
394
 
366
- reconnect() {
367
- this.adapter.setAdapterConnectionState(false);
395
+ scheduleReconnect() {
396
+ if (this.connectionStatus === commonDefines.ConnectionStatus.WAITING_RETRY) {
397
+ return;
398
+ }
399
+
400
+ const delay = this.retryCounter < maxRetries ? reconnectInterval : retryInterval;
368
401
 
369
402
  if (this.retryCounter < maxRetries) {
370
403
  this.retryCounter++;
371
- this.adapter.log.warn(`Reconnecting (try ${this.retryCounter} of ${maxRetries})...`);
372
- this.adapter.setTimeout(() => this.Connect(), reconnectInterval);
373
- } else {
374
- this.adapter.log.warn(
375
- `Connection to gateway lost, connection temporarily disabled! Trying again in ${retryInterval / (60 * 1000)} minutes.`,
376
- );
377
- this.adapter.setTimeout(() => this.Connect(), retryInterval);
378
404
  }
405
+
406
+ this.connectionStatus = commonDefines.ConnectionStatus.WAITING_RETRY;
407
+
408
+ this.adapter.log.warn(
409
+ this.retryCounter <= maxRetries
410
+ ? `Reconnecting (try ${this.retryCounter} of ${maxRetries})...`
411
+ : `Gateway offline. Retrying in ${delay / 60000} minutes.`,
412
+ );
413
+
414
+ if (this.reconnectTimer) {
415
+ this.adapter.clearTimeout(this.reconnectTimer);
416
+ }
417
+
418
+ this.reconnectTimer = this.adapter.setTimeout(() => {
419
+ this.reconnectTimer = null;
420
+ this.Connect();
421
+ }, delay);
379
422
  }
380
423
 
381
424
  queueUpPromise(promise) {
@@ -449,16 +492,5 @@ class SchellenbergBridge {
449
492
  }
450
493
  }
451
494
  }
452
-
453
- renewSocket(reason) {
454
- if (this.stopRenewal) {
455
- return;
456
- }
457
-
458
- this.adapter.log.warn(`Socket renewal was requested with reason: ${reason}`);
459
- this.adapter.setTimeout(() => {
460
- this.Connect();
461
- }, 1000);
462
- }
463
495
  }
464
496
  exports.SchellenbergBridge = SchellenbergBridge;
@@ -109,20 +109,17 @@ class SmartSocket {
109
109
  }
110
110
  });
111
111
  this.internalSocket.on("timeout", () => {
112
- this.adapter.log.warn("Socket timed out");
113
- this.adapter.setTimeout(() => {
114
- if (this.dataDelegate) {
115
- this.dataDelegate.renewSocket("Timeout");
116
- }
117
- }, 1000);
112
+ this.adapter.log.warn("Socket timeout (no activity).");
113
+
114
+ // Nur Keepalive schicken statt reconnect
115
+ if (this.dataDelegate && this.dataDelegate.sendCommand) {
116
+ this.adapter.log.debug("Sending keepalive due to socket timeout.");
117
+ this.dataDelegate.sendCommand(CommandFactory.default.createKeepAliveCmd());
118
+ }
118
119
  });
119
120
  this.internalSocket.on("end", () => {
121
+ // just log end event, as close event will be triggered afterwards and handle the disconnect
120
122
  this.adapter.log.warn("Socket ended");
121
- setImmediate(() => {
122
- if (this.dataDelegate) {
123
- this.dataDelegate.handleDisconnect();
124
- }
125
- });
126
123
  });
127
124
  this.internalSocket.on("close", () => {
128
125
  this.adapter.log.warn("Socket closed");
@@ -106,7 +106,9 @@ class DeviceManager {
106
106
 
107
107
  await this.adapter.setStateAsync(id, false, true);
108
108
  } catch (error) {
109
- this.log.error(`Error in handleStateChange: ${error.responseMessage}`);
109
+ this.log.error(
110
+ `Error in handleStateChange(id=${id}, state=${JSON.stringify(state)}): ${error?.message || JSON.stringify(error)}`,
111
+ );
110
112
  }
111
113
  }
112
114
 
@@ -120,7 +122,9 @@ class DeviceManager {
120
122
 
121
123
  await this.bridge.sendAndReceiveCommand(commandFactory.default.createSetDeviceValueCmd(deviceId, value));
122
124
  } catch (error) {
123
- this.log.error(`Error in sendDeviceCommand: ${error.responseMessage}`);
125
+ this.log.error(
126
+ `Error in sendDeviceCommand(deviceId=${deviceId}, value=${value}): ${error?.message || JSON.stringify(error)}`,
127
+ );
124
128
  throw error;
125
129
  }
126
130
  }
@@ -20,3 +20,10 @@ exports.AdapterStateIDs = Object.freeze({
20
20
  // devices.XXX.control
21
21
  Position: "position",
22
22
  });
23
+
24
+ exports.ConnectionStatus = {
25
+ IDLE: "idle",
26
+ CONNECTING: "connecting",
27
+ CONNECTED: "connected",
28
+ WAITING_RETRY: "waiting_retry",
29
+ };
package/main.js CHANGED
@@ -223,7 +223,7 @@ class Smartfriends extends utils.Adapter {
223
223
  onUnload(callback) {
224
224
  try {
225
225
  if (SchellenbergBridge != null) {
226
- SchellenbergBridge.handleDisconnect();
226
+ SchellenbergBridge.handleDisconnect(true);
227
227
  }
228
228
 
229
229
  this.setAdapterConnectionState(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.smartfriends",
3
- "version": "1.3.5",
3
+ "version": "1.3.6-alpha.1",
4
4
  "description": "smartfriends",
5
5
  "author": {
6
6
  "name": "Black-Thunder",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@alcalzone/release-script": "^5.1.1",
30
- "@alcalzone/release-script-plugin-iobroker": "^5.1.1",
30
+ "@alcalzone/release-script-plugin-iobroker": "^5.1.2",
31
31
  "@alcalzone/release-script-plugin-license": "^5.1.1",
32
32
  "@alcalzone/release-script-plugin-manual-review": "^5.1.1",
33
33
  "@iobroker/adapter-dev": "^1.5.0",