iobroker.anthbot-genie 0.1.2 → 0.1.3

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
@@ -272,8 +272,9 @@ Special credit to the Home Assistant Anthbot Genie projects, which made the Anth
272
272
  This ioBroker adapter is an independent project, but it builds on public API research and implementation ideas from that Home Assistant integration.
273
273
 
274
274
  ## Changelog
275
+ ### 0.1.3 (2026-05-08)
275
276
 
276
- ### **WORK IN PROGRESS**
277
+ - Fix AWS IoT shadow access by using temporary Anthbot IoT credentials instead of the expired bundled AWS credentials.
277
278
 
278
279
  ### 0.1.2
279
280
 
@@ -296,49 +297,10 @@ This ioBroker adapter is an independent project, but it builds on public API res
296
297
  - Fix near-charger mowing enable control to use the mower shadow setting.
297
298
  - Remove unsupported camera-enabled and docking resume-return controls.
298
299
 
299
- ### 0.1.0-beta.2
300
-
301
- - Add full-map mowing control to include edge trimming.
302
- - Remove the unsupported camera-enabled control.
303
- - Fix near-charger mowing enable control to use the mower shadow setting.
304
- - Remove the docking resume-return command because the cloud command is not working reliably.
305
-
306
- ### 0.1.0-beta.1
307
-
308
- - Add expanded diagnostics for model names, region fallback, errors, RTK, map, firmware, OTA, network, and GPS/location data.
309
- - Correct consumable maintenance mapping to blades, cameras, and charging port.
310
- - Add consumable reset buttons for charging port, cameras, and blades.
311
- - Remove metric states duplicated by writable controls and group mowing controls by full-map, zone, and near-charger mowing.
312
- - Group command states by device, docking, maintenance, and mowing with consistent action names.
313
- - Refactor state layout into grouped metrics, diagnostics, consumables, zones, raw shadows, and rain controls while keeping single-entry controls flat.
314
-
315
- ### 0.1.0-beta.0
316
-
317
- - Add mower action commands: find robot, grass dump, disk maintenance mode, edge mowing, near-charger mowing, and point mowing.
318
- - Add task control commands: pause/continue mowing, pause/continue return-to-dock, and end mowing.
319
- - Add RTK antenna moved warning cancel command.
320
- - Add status and control states for mowing near the charging pile, including its mowing parameters.
321
- - Add camera switch status and control.
322
- - Add RTK antenna moved warning status.
323
-
324
300
  ### 0.0.8
325
301
 
326
302
  - Add consumable channels and values ​​to the adapter definition.
327
303
 
328
- ### 0.0.7
329
-
330
- - Add Dependabot automerge configuration.
331
- - Update repository metadata for ioBroker checks.
332
-
333
- ### 0.0.6
334
-
335
- - Fix ioBroker repository checker issues.
336
- - Move admin configuration translations to i18n files.
337
-
338
- ### 0.0.5
339
-
340
- - Prepare adapter metadata for ioBroker repository checks.
341
-
342
304
  ## License
343
305
 
344
306
  MIT License
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "anthbot-genie",
4
- "version": "0.1.2",
4
+ "version": "0.1.3",
5
5
  "news": {
6
+ "0.1.3": {
7
+ "en": "Fix AWS IoT shadow access.",
8
+ "de": "AWS IoT-Shadow-Zugriff korrigiert.",
9
+ "ru": "Исправлен доступ к AWS IoT shadow.",
10
+ "pt": "Corrigido o acesso ao shadow AWS IoT.",
11
+ "nl": "AWS IoT-shadowtoegang opgelost.",
12
+ "fr": "Correction de l'accès au shadow AWS IoT.",
13
+ "it": "Corretto l'accesso allo shadow AWS IoT.",
14
+ "es": "Corregido el acceso al shadow de AWS IoT.",
15
+ "pl": "Naprawiono dostęp do shadow AWS IoT.",
16
+ "uk": "Виправлено доступ до AWS IoT shadow.",
17
+ "zh-cn": "修复 AWS IoT shadow 访问。"
18
+ },
6
19
  "0.1.2": {
7
20
  "en": "Limit io-package news entries for the ioBroker repository builder.",
8
21
  "de": "Anzahl der io-package-News-Einträge für den ioBroker-Repository-Builder begrenzt.",
@@ -42,45 +55,6 @@
42
55
  "uk": "Додано розширену діагностику, згруповану структуру станів, скидання витратних матеріалів, команди дій косарки та записувані елементи керування косінням.\nДодано керування обрізанням країв для повного косіння карти та виправлено керування біля зарядної станції.\nВилучено непідтримувані елементи керування камерою та продовженням повернення до бази.",
43
56
  "zh-cn": "添加扩展诊断、分组状态结构、耗材重置、割草机动作命令和可写割草控制。\n添加全地图割草的边缘修剪控制,并修正充电站附近割草启用控制。\n移除不支持的摄像头和继续回充控制。"
44
57
  },
45
- "0.1.0-beta.2": {
46
- "en": "Add full-map edge trimming control.\nRemove unsupported camera and docking resume controls.\nFix near-charger mowing enable control to use the mower shadow setting.",
47
- "de": "Vollflächen-Kantenschnitt-Steuerung hinzugefügt.\nNicht unterstützte Kamera- und Docking-Fortsetzen-Steuerungen entfernt.\nSteuerung zum Aktivieren des Mähens nahe der Ladestation auf die Mäher-Shadow-Einstellung korrigiert.",
48
- "ru": "Добавлено управление обрезкой краев для полного кошения карты.\nУдалены неподдерживаемые элементы управления камерой и продолжением возврата на базу.\nИсправлено включение кошения возле зарядной станции через настройку shadow косилки.",
49
- "pt": "Adicionado controle de corte de borda para corte de mapa completo.\nRemovidos controles não suportados de câmera e retomada do retorno à base.\nCorrigido o controle de ativação do corte perto da estação para usar a configuração shadow do cortador.",
50
- "nl": "Besturing voor randmaaien bij volledig kaartmaaien toegevoegd.\nNiet-ondersteunde camera- en docking-hervatbesturing verwijderd.\nBesturing voor maaien nabij het laadstation gebruikt nu de shadow-instelling van de maaier.",
51
- "fr": "Ajout du contrôle de coupe des bordures pour la tonte de toute la carte.\nSuppression des contrôles non pris en charge pour la caméra et la reprise du retour à la station.\nCorrection du contrôle d'activation de la tonte près de la station avec le réglage shadow de la tondeuse.",
52
- "it": "Aggiunto il controllo del taglio dei bordi per il taglio completo della mappa.\nRimossi i controlli non supportati per fotocamera e ripresa del ritorno alla base.\nCorretto il controllo di attivazione del taglio vicino alla stazione usando l'impostazione shadow del rasaerba.",
53
- "es": "Añadido control de corte de bordes para el corte de mapa completo.\nEliminados los controles no compatibles de cámara y reanudación del retorno a la base.\nCorregido el control para activar el corte cerca de la estación usando la configuración shadow del cortacésped.",
54
- "pl": "Dodano sterowanie koszeniem krawędzi dla pełnego koszenia mapy.\nUsunięto nieobsługiwane sterowanie kamerą i wznawianiem powrotu do stacji.\nPoprawiono włączanie koszenia przy stacji, aby używało ustawienia shadow kosiarki.",
55
- "uk": "Додано керування обрізанням країв для повного косіння карти.\nВилучено непідтримувані елементи керування камерою та продовженням повернення до бази.\nВиправлено вмикання косіння біля зарядної станції через налаштування shadow косарки.",
56
- "zh-cn": "添加全地图割草的边缘修剪控制。\n移除不支持的摄像头和继续回充控制。\n修正充电站附近割草启用控制,改为使用割草机 shadow 设置。"
57
- },
58
- "0.1.0-beta.0": {
59
- "en": "Add consumable reset buttons for station, cameras, and blades.\nAdd mower action commands: find robot, grass dump, disk maintenance mode, edge mowing, near-charger mowing, and point mowing.\nAdd task control commands: pause/continue mowing, pause/continue return-to-dock, and end mowing.\nAdd RTK antenna moved warning cancel command.\nAdd status and control states for mowing near the charging pile, including its mowing parameters.\nAdd camera switch status and control.\nAdd RTK antenna moved warning status.",
60
- "de": "Hinzufügen von Verbrauchs-Reset-Tasten für Station, Kameras und Klingen.\nFügen Sie Mäher-Action-Befehle hinzu: Finden Sie Roboter, Gras-Dump, Festplatten-Wartungsmodus, Rand Mähen, Nah-Ladegerät Mähen und Punkt Mähen.\nBefehle zur Task-Steuerung hinzufügen: Pause/Kontinue-Mähen, Pause/Fortsetzen-Rückkehr-zu-Dock und Ende-Mähen.\nFügen Sie RTK-Antenne bewegte Warnung Löschbefehl.\nFügen Sie Status- und Steuerzustände zum Mähen in der Nähe des Ladestapels hinzu, einschließlich seiner Mähparameter.\nFügen Sie Kameraschalter Status und Steuerung hinzu.\nFügen Sie RTK-Antenne bewegten Warnstatus hinzu.",
61
- "ru": "Добавьте расходные кнопки сброса для станции, камер и лопастей.\nДобавьте команды действия косилки: найдите робота, свалку травы, режим обслуживания диска, косилку ребра, косилку около зарядного устройства и точечную косилку.\nДобавьте команды управления задачами: пауза / продолжение скашивания, пауза / продолжение возврата в док и конец скашивания.\nДобавить антенну RTK с перемещенным предупреждением отменить команду.\nДобавить состояния состояния и контроля для скашивания вблизи зарядной кучи, включая ее параметры скашивания.\nДобавьте статус переключателя камеры и управление.\nДобавьте антенну RTK с измененным статусом предупреждения.",
62
- "pt": "Adicione botões de reset consumíveis para estações, câmeras e lâminas.\nAdicionar comandos de ação do cortador de grama: encontrar robô, despejo de grama, modo de manutenção de disco, corte de borda, corte perto do carregador, e corte ponto.\nAdicionar comandos de controle de tarefa: pausar/continuar o corte, pausar/continuar o retorno à doca e terminar o corte.\nAdicionar o comando de cancelamento de aviso movido da antena RTK.\nAdicione status e estados de controle para cortar perto da pilha de carregamento, incluindo seus parâmetros de corte.\nAdicione status e controle do interruptor de câmera.\nAdicionar o estado de aviso movido da antena RTK.",
63
- "nl": "Voeg verbruiksresetknoppen voor station, camera's en messen toe.\nVoeg maaier actie commando's: vind robot, gras dump, schijf onderhoud modus, rand maaien, bijna-charger maaien, en punt maaien.\nVoeg taakbeheeropdrachten toe: pauzeer/ continu maaien, pauzeer/continue return-to-dock en eind maaien.\nVoeg RTK antenne verplaatste waarschuwing annuleren commando.\nVoeg status en controle toestanden voor maaien in de buurt van de laadstapel, inclusief de maaiparameters.\nVoeg camera switch status en controle.\nVoeg RTK antenne verplaatste waarschuwingsstatus toe.",
64
- "fr": "Ajoutez des boutons de réinitialisation consommables pour la station, les caméras et les lames.\nAjouter des commandes d'action de tondeuse : trouver le robot, la décharge d'herbe, le mode d'entretien du disque, le tondage des bords, le tondage du chargeur et le tondage des points.\nAjoutez les commandes de contrôle des tâches : pause/continuer le tondage, pause/continuer le retour à l'aiguillage et fin du tondage.\nAjouter une antenne RTK déplacée avertissement annuler la commande.\nAjouter l'état et les états de contrôle pour tondre près du tas de charge, y compris ses paramètres de tonte.\nAjouter l'état et le contrôle du commutateur de caméra.\nAjouter l'état d'avertissement déplacé de l'antenne RTK.",
65
- "it": "Aggiungi pulsanti di ripristino dei materiali di consumo per stazioni, telecamere e lame.\nAggiungi comandi di azione del falciatore: trovare robot, discarica di erba, modalità di manutenzione del disco, mowing del bordo, mowing del quasi-charger e mowing del punto.\nAggiungi comandi di controllo dell'attività: pausa/continua la cucitura, pausa/continua il rientro al dock e la falciatura finale.\nAggiungere RTK antenna spostato avviso disdetto comando.\nAggiungere stati di stato e di controllo per il cucito vicino alla pila di ricarica, compresi i suoi parametri di cucito.\nAggiungi lo stato e il controllo dell'interruttore della fotocamera.\nAggiungere RTK antenna spostato stato di avvertimento.",
66
- "es": "Agregue botones de reinicio consumibles para estaciones, cámaras y cuchillas.\nAñadir comandos de acción de la cortadora: encontrar robot, vertedero de césped, modo de mantenimiento de disco, mowing de borde, mowing de carga cercana, y mowing de puntos.\nAgregue comandos de control de tareas: pausa/continua mowing, pausa/continua retorno-a-dock, y mowing final.\nAñadir la antena RTK movió el comando de cancelación de advertencia.\nAgregue estados de estado y control para moverse cerca de la pila de carga, incluyendo sus parámetros de movimiento.\nAgregue el estado y el control del interruptor de cámara.\nAñadir la antena RTK movió el estado de advertencia.",
67
- "pl": "Dodaj zużywalne przyciski resetujące dla stacji, kamer i ostrzy.\nDodaj polecenia działania kosiarki: znajdź robota, trawę, tryb konserwacji dysku, koszenie krawędzi, koszenie w pobliżu ładowarki i koszenie punktowe.\nDodaj polecenia kontroli zadania: pauza / kontynuuj koszenie, pauza / kontynuuj return-to- dock i koszenie końcowe.\nDodać RTK antena przeniesione ostrzeżenie anulować polecenie.\nDodać stany stanu i kontroli koszenia w pobliżu stosu ładowania, w tym jego parametry koszenia.\nDodaj status i sterowanie przełącznika kamery.\nDodaj status ostrzegawczy anteny RTK.",
68
- "uk": "Додайте примітні кнопки скидання для станції, камер і леза.\nДодайте команди косарки: знайти роботу, травне скидання, режим технічного обслуговування диска, кромочні ковтки, приблизний ковток, а також точку ковтки.\nДодайте команди керування завданнями: пауза / безперервне скошування, пауза/переконайте зворотний дзвінок, а також кінцеве скошування.\nДодати RTK антену перемістив команду скасування попередження.\nДодайте статус і контрольні стани для скошування під час зарядки, в тому числі його параметри скошування.\nДодайте статус вимикача камери та контроль.\nДодати RTK антена перемістив статус попередження.",
69
- "zh-cn": "增加空间站、摄像头和刀片的消耗性重置按钮.\n添加割草机动作命令:找到机器人,草堆,磁盘维护模式,剪边,近充机割草,点割.\n添加任务控制命令: 暂停/ 继续剪切, 暂停/ 继续折返到.\n添加 RTK 天线移动警告取消命令 .\n增加电荷堆附近割草的状态和控制状态,包括其割草参数.\n添加相机开关状态及控制.\n添加 RTK 天线移动警告状态 ."
70
- },
71
- "0.0.9-beta.0": {
72
- "en": "Add mower service commands and controls.",
73
- "de": "Mäher-Servicebefehle und Steuerungen hinzugefügt.",
74
- "ru": "Добавлены сервисные команды и элементы управления косилкой.",
75
- "pt": "Adicionados comandos de serviço e controles do cortador.",
76
- "nl": "Maaier-serviceopdrachten en bedieningselementen toegevoegd.",
77
- "fr": "Ajout de commandes de service et de contrôles pour la tondeuse.",
78
- "it": "Aggiunti comandi di servizio e controlli del rasaerba.",
79
- "es": "Se añadieron comandos de servicio y controles del cortacésped.",
80
- "pl": "Dodano polecenia serwisowe i sterowanie kosiarką.",
81
- "uk": "Додано сервісні команди та елементи керування косаркою.",
82
- "zh-cn": "添加割草机服务命令和控制。"
83
- },
84
58
  "0.0.8": {
85
59
  "en": "Add consumable channels and values to the adapter definition.",
86
60
  "de": "Verbrauchskanäle und Werte zur Adapterdefinition hinzugefügt.",
@@ -109,7 +83,7 @@
109
83
  "zh-cn": "Anthbot Genie"
110
84
  },
111
85
  "desc": {
112
- "en": "Unofficial cloud adapter for Anthbot Genie mower control and status polling",
86
+ "en": "Unofficial cloud adapter for Anthbot Genie robotic lawn mowers with telemetry, diagnostics, consumables, zones, and controls",
113
87
  "de": "Inoffizieller Cloud-Adapter für Statusabfrage und Steuerung von Anthbot Genie",
114
88
  "ru": "Неофициальный облачный адаптер для управления газонокосилками Anthbot Genie и опроса состояния",
115
89
  "pt": "Adaptador de nuvem não oficial para controlar cortadores Anthbot Genie e consultar o estado",
@@ -133,10 +107,14 @@
133
107
  "readme": "https://github.com/reloxx13/ioBroker.anthbot-genie/blob/main/README.md",
134
108
  "keywords": [
135
109
  "anthbot",
136
- "genie",
110
+ "anthbot-genie",
111
+ "robotic-lawn-mower",
112
+ "lawn-mower",
137
113
  "mower",
138
- "robot",
139
- "lawn"
114
+ "home-automation",
115
+ "rtk",
116
+ "cloud-api",
117
+ "iot"
140
118
  ],
141
119
  "platform": "Javascript/Node.js",
142
120
  "mode": "daemon",
package/lib/anthbot.js CHANGED
@@ -335,6 +335,51 @@ class AnthbotCloudApiClient {
335
335
  };
336
336
  }
337
337
 
338
+ async getDeviceIotCredentials(serialNumber) {
339
+ this.requireToken();
340
+ const response = await this.http.post(`https://${this.host}/api/v1/device/v2/iot/sts/arn`, {
341
+ sn: serialNumber,
342
+ verification_token: AnthbotCloudApiClient.buildVerificationToken(serialNumber),
343
+ }, {
344
+ headers: {
345
+ ...this.authHeaders,
346
+ "content-type": "application/json",
347
+ },
348
+ });
349
+ const payload = response.data;
350
+ if (response.status !== 200) {
351
+ throw new AnthbotGenieError(`IoT STS failed (${response.status}): ${String(payload).slice(0, 300)}`);
352
+ }
353
+ if (!payload || typeof payload !== "object") {
354
+ throw new AnthbotGenieError("Invalid IoT STS payload type");
355
+ }
356
+ if (payload.code !== 0) {
357
+ throw new AnthbotGenieError(`IoT STS returned code=${JSON.stringify(payload.code)}`);
358
+ }
359
+ const data = payload.data;
360
+ if (!data || typeof data !== "object") {
361
+ throw new AnthbotGenieError("IoT STS payload missing data object");
362
+ }
363
+ const requiredFields = ["access_key_id", "secret_access_key", "session_token", "region_name", "endpoint"];
364
+ if (requiredFields.some(field => typeof data[field] !== "string" || !data[field])) {
365
+ throw new AnthbotGenieError("IoT STS payload missing required fields");
366
+ }
367
+ const expiration = asInteger(data.expiration);
368
+ const expiresAt = expiration == null
369
+ ? null
370
+ : expiration > 2000000000
371
+ ? expiration * 1000
372
+ : Date.now() + expiration * 1000;
373
+ return {
374
+ accessKeyId: data.access_key_id,
375
+ secretAccessKey: data.secret_access_key,
376
+ sessionToken: data.session_token,
377
+ regionName: data.region_name,
378
+ endpoint: data.endpoint,
379
+ expiresAt,
380
+ };
381
+ }
382
+
338
383
  async getDeviceAreaDefinition(serialNumber) {
339
384
  this.requireToken();
340
385
  const response = await this.http.get(`https://${this.host}/api/v1/device/v2/presigned_url`, {
@@ -427,11 +472,12 @@ class AnthbotCloudApiClient {
427
472
  }
428
473
 
429
474
  class AnthbotShadowApiClient {
430
- constructor({ http, serialNumber, regionName, iotEndpoint }) {
475
+ constructor({ http, serialNumber, regionName, iotEndpoint, iotCredentials = null }) {
431
476
  this.http = http;
432
477
  this.serialNumber = serialNumber;
433
478
  this.regionName = typeof regionName === "string" && regionName ? regionName : null;
434
479
  this.iotEndpoint = AnthbotShadowApiClient.normalizeEndpoint(iotEndpoint);
480
+ this.iotCredentials = iotCredentials && typeof iotCredentials === "object" ? iotCredentials : null;
435
481
  }
436
482
 
437
483
  static normalizeEndpoint(iotEndpoint) {
@@ -459,6 +505,9 @@ class AnthbotShadowApiClient {
459
505
  }
460
506
 
461
507
  accessKeyId() {
508
+ if (typeof this.iotCredentials?.accessKeyId === "string" && this.iotCredentials.accessKeyId) {
509
+ return this.iotCredentials.accessKeyId;
510
+ }
462
511
  if (this.iotEndpoint === CN_NORTHWEST_IOT_ENDPOINT) {
463
512
  return AWS_ACCESS_KEY_CN_NORTHWEST;
464
513
  }
@@ -469,6 +518,9 @@ class AnthbotShadowApiClient {
469
518
  }
470
519
 
471
520
  secretAccessKey() {
521
+ if (typeof this.iotCredentials?.secretAccessKey === "string" && this.iotCredentials.secretAccessKey) {
522
+ return this.iotCredentials.secretAccessKey;
523
+ }
472
524
  if (this.iotEndpoint === CN_NORTHWEST_IOT_ENDPOINT) {
473
525
  return AWS_SECRET_KEY_CN_NORTHWEST;
474
526
  }
@@ -478,6 +530,12 @@ class AnthbotShadowApiClient {
478
530
  return AWS_SECRET_KEY_DEFAULT;
479
531
  }
480
532
 
533
+ sessionToken() {
534
+ return typeof this.iotCredentials?.sessionToken === "string" && this.iotCredentials.sessionToken
535
+ ? this.iotCredentials.sessionToken
536
+ : null;
537
+ }
538
+
481
539
  sign(key, msg) {
482
540
  return crypto.createHmac("sha256", key).update(msg, "utf8").digest();
483
541
  }
@@ -557,6 +615,10 @@ class AnthbotShadowApiClient {
557
615
  "x-amz-content-sha256": payloadHash,
558
616
  "x-amz-date": amzDate,
559
617
  };
618
+ const sessionToken = this.sessionToken();
619
+ if (sessionToken) {
620
+ signedHeaderValues["x-amz-security-token"] = sessionToken;
621
+ }
560
622
  const { canonical, signedHeaders } = AnthbotShadowApiClient.canonicalHeaders(signedHeaderValues);
561
623
  const canonicalRequest = [
562
624
  "GET",
@@ -573,6 +635,7 @@ class AnthbotShadowApiClient {
573
635
  Host: this.iotEndpoint,
574
636
  "x-amz-date": amzDate,
575
637
  "x-amz-content-sha256": payloadHash,
638
+ ...(sessionToken ? { "x-amz-security-token": sessionToken } : {}),
576
639
  Authorization: authorization,
577
640
  "User-Agent": "LdMower/1581 CFNetwork/3860.400.51 Darwin/25.3.0",
578
641
  },
@@ -617,6 +680,11 @@ class AnthbotShadowApiClient {
617
680
  "x-amz-content-sha256": payloadHash,
618
681
  "x-amz-date": amzDate,
619
682
  };
683
+ const sessionToken = this.sessionToken();
684
+ if (sessionToken) {
685
+ signedHeaderValues["x-amz-security-token"] = sessionToken;
686
+ headers["x-amz-security-token"] = sessionToken;
687
+ }
620
688
  if (signContentLength) {
621
689
  signedHeaderValues["content-length"] = String(payloadBytes.length);
622
690
  headers["Content-Length"] = String(payloadBytes.length);
package/main.js CHANGED
@@ -402,7 +402,9 @@ class AnthbotGenieAdapter extends utils.Adapter {
402
402
  serialNumber: device.serialNumber,
403
403
  regionName: region.regionName,
404
404
  iotEndpoint: region.iotEndpoint,
405
+ iotCredentials: region.iotCredentials,
405
406
  }),
407
+ iotCredentials: region.iotCredentials,
406
408
  areaDefinition: existing?.areaDefinition || {},
407
409
  lastAreaTime: existing?.lastAreaTime || null,
408
410
  lastReported: existing?.lastReported || {},
@@ -416,6 +418,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
416
418
  async resolveDeviceRegion(device) {
417
419
  let regionName = null;
418
420
  let iotEndpoint = null;
421
+ let iotCredentials = null;
419
422
 
420
423
  try {
421
424
  const deviceRegion = await this.cloudClient.getDeviceRegion(device.serialNumber);
@@ -446,10 +449,19 @@ class AnthbotGenieAdapter extends utils.Adapter {
446
449
  this.log.debug(`Presigned region fallback failed for ${device.serialNumber}: ${error.message}`);
447
450
  }
448
451
 
452
+ try {
453
+ iotCredentials = await this.cloudClient.getDeviceIotCredentials(device.serialNumber);
454
+ regionName = iotCredentials.regionName || regionName;
455
+ iotEndpoint = iotCredentials.endpoint || iotEndpoint;
456
+ } catch (error) {
457
+ this.log.warn(`Failed to fetch temporary IoT credentials for ${device.serialNumber}, using bundled fallback credentials: ${error.message}`);
458
+ }
459
+
449
460
  return {
450
461
  serialNumber: device.serialNumber,
451
462
  regionName: regionName || AnthbotShadowApiClient.guessRegionFromEndpoint(iotEndpoint) || "unknown",
452
463
  iotEndpoint,
464
+ iotCredentials,
453
465
  };
454
466
  }
455
467
 
@@ -500,6 +512,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
500
512
  }
501
513
 
502
514
  async refreshDevice(context) {
515
+ await this.ensureDeviceIotCredentials(context);
503
516
  const propertyState = await context.shadowClient.getShadowReportedState();
504
517
  let serviceState = {};
505
518
  try {
@@ -536,6 +549,27 @@ class AnthbotGenieAdapter extends utils.Adapter {
536
549
  await this.updateStates(context, merged);
537
550
  }
538
551
 
552
+ async ensureDeviceIotCredentials(context) {
553
+ if (context.iotCredentials && (!context.iotCredentials.expiresAt || context.iotCredentials.expiresAt - Date.now() > 60000)) {
554
+ return;
555
+ }
556
+ const iotCredentials = await this.cloudClient.getDeviceIotCredentials(context.device.serialNumber);
557
+ context.iotCredentials = iotCredentials;
558
+ context.region = {
559
+ ...context.region,
560
+ regionName: iotCredentials.regionName || context.region.regionName,
561
+ iotEndpoint: iotCredentials.endpoint || context.region.iotEndpoint,
562
+ iotCredentials,
563
+ };
564
+ context.shadowClient = new AnthbotShadowApiClient({
565
+ http: this.http,
566
+ serialNumber: context.device.serialNumber,
567
+ regionName: context.region.regionName,
568
+ iotEndpoint: context.region.iotEndpoint,
569
+ iotCredentials,
570
+ });
571
+ }
572
+
539
573
  async updateStates(context, data) {
540
574
  const serial = context.device.serialNumber;
541
575
  const manualZoneList = manualZones(data);
@@ -797,6 +831,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
797
831
  return;
798
832
  }
799
833
 
834
+ await this.ensureDeviceIotCredentials(context);
800
835
  const shouldRequestProperties = await this.executeCommand(context, command, value);
801
836
  if (shouldRequestProperties) {
802
837
  await context.shadowClient.requestAllProperties();
@@ -809,6 +844,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
809
844
  return;
810
845
  }
811
846
 
847
+ await this.ensureDeviceIotCredentials(context);
812
848
  await this.executeControl(context, control, value);
813
849
  await context.shadowClient.requestAllProperties();
814
850
  await this.delay(1000);
@@ -820,6 +856,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
820
856
  return;
821
857
  }
822
858
 
859
+ await this.ensureDeviceIotCredentials(context);
823
860
  await this.executeConsumableCommand(context, command);
824
861
  await this.delay(1000);
825
862
  }
package/package.json CHANGED
@@ -1,17 +1,24 @@
1
1
  {
2
2
  "name": "iobroker.anthbot-genie",
3
- "version": "0.1.2",
4
- "description": "Unofficial ioBroker adapter for Anthbot Genie mowers",
3
+ "version": "0.1.3",
4
+ "description": "Unofficial ioBroker adapter for Anthbot Genie robotic lawn mowers with cloud telemetry, diagnostics, consumables, zones, and controls.",
5
5
  "author": "reloxx13",
6
6
  "license": "MIT",
7
7
  "main": "main.js",
8
8
  "homepage": "https://github.com/reloxx13/ioBroker.anthbot-genie",
9
9
  "keywords": [
10
10
  "ioBroker",
11
+ "iobroker-adapter",
11
12
  "anthbot",
12
- "genie",
13
+ "anthbot-genie",
14
+ "robotic-lawn-mower",
15
+ "lawn-mower",
13
16
  "mower",
14
- "adapter"
17
+ "smart-home",
18
+ "home-automation",
19
+ "rtk",
20
+ "cloud-api",
21
+ "iot"
15
22
  ],
16
23
  "repository": {
17
24
  "type": "git",