iobroker.anthbot-genie 0.1.3 → 0.1.4
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 +11 -4
- package/admin/i18n/de/translations.json +3 -1
- package/admin/i18n/en/translations.json +3 -1
- package/admin/i18n/es/translations.json +3 -1
- package/admin/i18n/fr/translations.json +3 -1
- package/admin/i18n/it/translations.json +3 -1
- package/admin/i18n/nl/translations.json +3 -1
- package/admin/i18n/pl/translations.json +3 -1
- package/admin/i18n/pt/translations.json +3 -1
- package/admin/i18n/ru/translations.json +3 -1
- package/admin/i18n/uk/translations.json +3 -1
- package/admin/i18n/zh-cn/translations.json +3 -1
- package/admin/jsonConfig.json +64 -0
- package/io-package.json +17 -3
- package/lib/anthbot.js +73 -59
- package/main.js +93 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,13 +30,13 @@ An example ioBroker Blockly with conditions for mower automation is available in
|
|
|
30
30
|
- Region and IoT endpoint lookup per mower
|
|
31
31
|
- Polling of property and service shadows
|
|
32
32
|
- Detailed status states for connection, online state, battery, mower status, charging state, mowing time, mowing area, map status, errors, active mowing mode, point mowing, and zone counts
|
|
33
|
-
- Diagnostic states for RTK fix, RTK base station, moved antenna warnings, firmware versions, OTA progress, WiFi, cellular, SIM, Bluetooth, camera/map flags, obstacle avoidance, security flags, system timestamps, and mower error data
|
|
33
|
+
- Diagnostic states for RTK fix, RTK base station, moved antenna warnings, firmware versions, OTA progress, WiFi, cellular, SIM, Bluetooth, camera/map flags, obstacle avoidance, security flags, system timestamps, and cloud-backed mower error data
|
|
34
34
|
- Location states for anti-loss GPS coordinates and local mower pose
|
|
35
35
|
- Consumable lifetime states and reset buttons for charging port, cameras, and blades
|
|
36
36
|
- Writable control states for full-map mowing, zone mowing, cutting height, voice volume, custom mowing direction, obstacle avoidance, rain settings, and mowing near the charging pile
|
|
37
37
|
- Command states for full mowing, stop, return to dock, pause return to dock, grass dump, disk maintenance mode, edge mowing, mowing near the charging pile, point mowing, refresh, manual zone mowing, and automatic zone mowing
|
|
38
38
|
- Manual and automatic zone metadata as JSON states, including active manual zone IDs
|
|
39
|
-
- Raw property shadow, service shadow, and area definition payloads for troubleshooting and automation debugging
|
|
39
|
+
- Raw property shadow, service shadow, Anthbot event-code translations, and area definition payloads for troubleshooting and automation debugging
|
|
40
40
|
|
|
41
41
|
## Requirements
|
|
42
42
|
|
|
@@ -56,7 +56,8 @@ Open the adapter instance configuration in ioBroker Admin and set:
|
|
|
56
56
|
| Anthbot account password | Anthbot account password, stored encrypted by ioBroker | empty |
|
|
57
57
|
| Area code | Phone or account area code, for example `49` for Germany | `49` |
|
|
58
58
|
| API host | Anthbot cloud API host | `api.anthbot.com` |
|
|
59
|
-
| Poll interval in seconds | Polling interval for mower data. The adapter enforces at least 10 seconds. | `
|
|
59
|
+
| Poll interval in seconds | Polling interval for mower data. The adapter enforces at least 10 seconds. | `60` |
|
|
60
|
+
| Error description language | Language used for Anthbot cloud error descriptions | `English` |
|
|
60
61
|
|
|
61
62
|
After saving the configuration, start or restart the adapter instance.
|
|
62
63
|
|
|
@@ -102,7 +103,7 @@ anthbot-genie.<instance>.<serial>.*
|
|
|
102
103
|
| `<serial>.metrics.map.totalArea` | number | `m2` | Total mapped area |
|
|
103
104
|
| `<serial>.metrics.map.status` | string | | Raw map status |
|
|
104
105
|
| `<serial>.metrics.error.code` | number | | Last mower error code |
|
|
105
|
-
| `<serial>.metrics.error.description` | string | | Human-readable error description when known |
|
|
106
|
+
| `<serial>.metrics.error.description` | string | | Human-readable error description from the cached Anthbot event-code list when known |
|
|
106
107
|
| `<serial>.metrics.error.active` | boolean | | Whether a non-zero mower error is active |
|
|
107
108
|
|
|
108
109
|
### Location
|
|
@@ -198,6 +199,7 @@ Availability of `commands.maintenance.startDiskMaintenance`, `commands.maintenan
|
|
|
198
199
|
| --- | --- | --- |
|
|
199
200
|
| `<serial>.raw.shadow.property` | JSON string | Raw property shadow payload |
|
|
200
201
|
| `<serial>.raw.shadow.service` | JSON string | Raw service shadow payload |
|
|
202
|
+
| `<serial>.raw.shadow.event-code` | JSON string | Cached Anthbot event-code translation payload used for error descriptions |
|
|
201
203
|
| `<serial>.raw.areaDefinition` | JSON string | Raw area definition payload |
|
|
202
204
|
|
|
203
205
|
## Zone Mowing
|
|
@@ -272,6 +274,11 @@ Special credit to the Home Assistant Anthbot Genie projects, which made the Anth
|
|
|
272
274
|
This ioBroker adapter is an independent project, but it builds on public API research and implementation ideas from that Home Assistant integration.
|
|
273
275
|
|
|
274
276
|
## Changelog
|
|
277
|
+
### 0.1.4 (2026-05-08)
|
|
278
|
+
|
|
279
|
+
- Use Anthbot cloud event-code translations for mower error descriptions and add a configurable description language.
|
|
280
|
+
- Store the fetched event-code translation cache in `raw.shadow.event-code` for troubleshooting.
|
|
281
|
+
|
|
275
282
|
### 0.1.3 (2026-05-08)
|
|
276
283
|
|
|
277
284
|
- Fix AWS IoT shadow access by using temporary Anthbot IoT credentials instead of the expired bundled AWS credentials.
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Ländervorwahl",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Beispiele: 49 für Deutschland, 32 für Belgien, 1 für USA/Kanada",
|
|
6
6
|
"API host": "API-Host",
|
|
7
|
-
"Poll interval in seconds": "Abfrageintervall in Sekunden"
|
|
7
|
+
"Poll interval in seconds": "Abfrageintervall in Sekunden",
|
|
8
|
+
"Error description language": "Sprache der Fehlerbeschreibung",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Sprache für Anthbot-Cloud-Fehlerbeschreibungen"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Area code",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada",
|
|
6
6
|
"API host": "API host",
|
|
7
|
-
"Poll interval in seconds": "Poll interval in seconds"
|
|
7
|
+
"Poll interval in seconds": "Poll interval in seconds",
|
|
8
|
+
"Error description language": "Error description language",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Language used for Anthbot cloud error descriptions"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Código de país",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Ejemplos: 49 para Alemania, 32 para Bélgica, 1 para EE. UU./Canadá",
|
|
6
6
|
"API host": "Host de la API",
|
|
7
|
-
"Poll interval in seconds": "Intervalo de consulta en segundos"
|
|
7
|
+
"Poll interval in seconds": "Intervalo de consulta en segundos",
|
|
8
|
+
"Error description language": "Idioma de la descripción del error",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Idioma utilizado para las descripciones de errores de la nube Anthbot"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Indicatif pays",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Exemples : 49 pour l'Allemagne, 32 pour la Belgique, 1 pour les États-Unis/Canada",
|
|
6
6
|
"API host": "Hôte de l'API",
|
|
7
|
-
"Poll interval in seconds": "Intervalle d'interrogation en secondes"
|
|
7
|
+
"Poll interval in seconds": "Intervalle d'interrogation en secondes",
|
|
8
|
+
"Error description language": "Langue de description des erreurs",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Langue utilisée pour les descriptions d'erreur du cloud Anthbot"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Prefisso internazionale",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Esempi: 49 per Germania, 32 per Belgio, 1 per USA/Canada",
|
|
6
6
|
"API host": "Host API",
|
|
7
|
-
"Poll interval in seconds": "Intervallo di interrogazione in secondi"
|
|
7
|
+
"Poll interval in seconds": "Intervallo di interrogazione in secondi",
|
|
8
|
+
"Error description language": "Lingua della descrizione errore",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Lingua usata per le descrizioni degli errori cloud Anthbot"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Landcode",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Voorbeelden: 49 voor Duitsland, 32 voor België, 1 voor VS/Canada",
|
|
6
6
|
"API host": "API-host",
|
|
7
|
-
"Poll interval in seconds": "Pollinginterval in seconden"
|
|
7
|
+
"Poll interval in seconds": "Pollinginterval in seconden",
|
|
8
|
+
"Error description language": "Taal voor foutbeschrijving",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Taal voor Anthbot-cloudfoutbeschrijvingen"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Kod kraju",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Przykłady: 49 dla Niemiec, 32 dla Belgii, 1 dla USA/Kanady",
|
|
6
6
|
"API host": "Host API",
|
|
7
|
-
"Poll interval in seconds": "Interwał odpytywania w sekundach"
|
|
7
|
+
"Poll interval in seconds": "Interwał odpytywania w sekundach",
|
|
8
|
+
"Error description language": "Język opisu błędów",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Język używany dla opisów błędów chmury Anthbot"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Código do país",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Exemplos: 49 para Alemanha, 32 para Bélgica, 1 para EUA/Canadá",
|
|
6
6
|
"API host": "Host da API",
|
|
7
|
-
"Poll interval in seconds": "Intervalo de consulta em segundos"
|
|
7
|
+
"Poll interval in seconds": "Intervalo de consulta em segundos",
|
|
8
|
+
"Error description language": "Idioma da descrição do erro",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Idioma usado para descrições de erro da nuvem Anthbot"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Код страны",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Примеры: 49 для Германии, 32 для Бельгии, 1 для США/Канады",
|
|
6
6
|
"API host": "Хост API",
|
|
7
|
-
"Poll interval in seconds": "Интервал опроса в секундах"
|
|
7
|
+
"Poll interval in seconds": "Интервал опроса в секундах",
|
|
8
|
+
"Error description language": "Язык описания ошибок",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Язык для описаний ошибок облака Anthbot"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "Код країни",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "Приклади: 49 для Німеччини, 32 для Бельгії, 1 для США/Канади",
|
|
6
6
|
"API host": "Хост API",
|
|
7
|
-
"Poll interval in seconds": "Інтервал опитування в секундах"
|
|
7
|
+
"Poll interval in seconds": "Інтервал опитування в секундах",
|
|
8
|
+
"Error description language": "Мова опису помилок",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "Мова для описів помилок хмари Anthbot"
|
|
8
10
|
}
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"Area code": "国家/地区代码",
|
|
5
5
|
"Examples: 49 for Germany, 32 for Belgium, 1 for US/Canada": "示例:德国为 49,比利时为 32,美国/加拿大为 1",
|
|
6
6
|
"API host": "API 主机",
|
|
7
|
-
"Poll interval in seconds": "轮询间隔(秒)"
|
|
7
|
+
"Poll interval in seconds": "轮询间隔(秒)",
|
|
8
|
+
"Error description language": "错误说明语言",
|
|
9
|
+
"Language used for Anthbot cloud error descriptions": "用于 Anthbot 云端错误说明的语言"
|
|
8
10
|
}
|
package/admin/jsonConfig.json
CHANGED
|
@@ -50,6 +50,70 @@
|
|
|
50
50
|
"md": 4,
|
|
51
51
|
"lg": 4,
|
|
52
52
|
"xl": 4
|
|
53
|
+
},
|
|
54
|
+
"errorDescriptionLanguage": {
|
|
55
|
+
"type": "select",
|
|
56
|
+
"label": "Error description language",
|
|
57
|
+
"help": "Language used for Anthbot cloud error descriptions",
|
|
58
|
+
"options": [
|
|
59
|
+
{
|
|
60
|
+
"label": "English",
|
|
61
|
+
"value": "English"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"label": "German",
|
|
65
|
+
"value": "German"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"label": "French",
|
|
69
|
+
"value": "French"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"label": "Spain",
|
|
73
|
+
"value": "Spain"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"label": "Italy",
|
|
77
|
+
"value": "Italy"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"label": "Polish",
|
|
81
|
+
"value": "Polish"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"label": "Russian",
|
|
85
|
+
"value": "Russian"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"label": "Czech",
|
|
89
|
+
"value": "Czech"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"label": "Estonian",
|
|
93
|
+
"value": "Estonian"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"label": "Latvian",
|
|
97
|
+
"value": "Latvian"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"label": "Lithuanian",
|
|
101
|
+
"value": "Lithuanian"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"label": "Romanian",
|
|
105
|
+
"value": "Romanian"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"label": "Slovakia",
|
|
109
|
+
"value": "Slovakia"
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"xs": 12,
|
|
113
|
+
"sm": 12,
|
|
114
|
+
"md": 4,
|
|
115
|
+
"lg": 4,
|
|
116
|
+
"xl": 4
|
|
53
117
|
}
|
|
54
118
|
}
|
|
55
119
|
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "anthbot-genie",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.4",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.1.4": {
|
|
7
|
+
"en": "Use Anthbot cloud event-code translations for mower error descriptions.",
|
|
8
|
+
"de": "Anthbot-Cloud-Ereigniscode-Übersetzungen für Mäher-Fehlerbeschreibungen verwendet.",
|
|
9
|
+
"ru": "Использованы облачные переводы кодов событий Anthbot для описаний ошибок косилки.",
|
|
10
|
+
"pt": "Usadas traduções de códigos de evento da nuvem Anthbot para descrições de erro do cortador.",
|
|
11
|
+
"nl": "Anthbot-cloudvertalingen van gebeurteniscodes gebruikt voor foutbeschrijvingen van de maaier.",
|
|
12
|
+
"fr": "Utilisation des traductions cloud Anthbot des codes d'événement pour les descriptions d'erreur de la tondeuse.",
|
|
13
|
+
"it": "Usate le traduzioni cloud Anthbot dei codici evento per le descrizioni degli errori del rasaerba.",
|
|
14
|
+
"es": "Se usan traducciones de códigos de evento de la nube Anthbot para descripciones de errores del cortacésped.",
|
|
15
|
+
"pl": "Użyto chmurowych tłumaczeń kodów zdarzeń Anthbot dla opisów błędów kosiarki.",
|
|
16
|
+
"uk": "Використано хмарні переклади кодів подій Anthbot для описів помилок косарки.",
|
|
17
|
+
"zh-cn": "使用 Anthbot 云端事件代码翻译生成割草机错误说明。"
|
|
18
|
+
},
|
|
6
19
|
"0.1.3": {
|
|
7
20
|
"en": "Fix AWS IoT shadow access.",
|
|
8
21
|
"de": "AWS IoT-Shadow-Zugriff korrigiert.",
|
|
@@ -119,7 +132,7 @@
|
|
|
119
132
|
"platform": "Javascript/Node.js",
|
|
120
133
|
"mode": "daemon",
|
|
121
134
|
"tier": 2,
|
|
122
|
-
"type": "
|
|
135
|
+
"type": "garden",
|
|
123
136
|
"compact": true,
|
|
124
137
|
"connectionType": "cloud",
|
|
125
138
|
"dataSource": "poll",
|
|
@@ -150,7 +163,8 @@
|
|
|
150
163
|
"password": "",
|
|
151
164
|
"areaCode": "49",
|
|
152
165
|
"apiHost": "api.anthbot.com",
|
|
153
|
-
"pollInterval":
|
|
166
|
+
"pollInterval": 60,
|
|
167
|
+
"errorDescriptionLanguage": "English"
|
|
154
168
|
},
|
|
155
169
|
"objects": [
|
|
156
170
|
{
|
package/lib/anthbot.js
CHANGED
|
@@ -20,63 +20,6 @@ const MODEL_NAME_BY_CATEGORY = {
|
|
|
20
20
|
"Genie 5000": "Anthbot Genie 5000",
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
const ERROR_CODE_DESCRIPTIONS = {
|
|
24
|
-
0: "No error",
|
|
25
|
-
1: "Battery low",
|
|
26
|
-
2: "Battery critically low",
|
|
27
|
-
3: "Battery over-temperature",
|
|
28
|
-
4: "Battery under-temperature",
|
|
29
|
-
5: "Battery overvoltage",
|
|
30
|
-
10: "Left wheel stuck",
|
|
31
|
-
11: "Right wheel stuck",
|
|
32
|
-
12: "Left wheel motor overload",
|
|
33
|
-
13: "Right wheel motor overload",
|
|
34
|
-
14: "Left wheel motor overheat",
|
|
35
|
-
15: "Right wheel motor overheat",
|
|
36
|
-
20: "Blade motor stuck",
|
|
37
|
-
21: "Blade motor overload",
|
|
38
|
-
22: "Blade motor overheat",
|
|
39
|
-
30: "Device lifted",
|
|
40
|
-
31: "Device tilted over limit",
|
|
41
|
-
32: "Device rollover",
|
|
42
|
-
33: "Device stuck",
|
|
43
|
-
40: "Bumper sensor triggered",
|
|
44
|
-
41: "Collision sensor jammed",
|
|
45
|
-
42: "ToF sensor fault",
|
|
46
|
-
43: "Structured-light sensor fault",
|
|
47
|
-
50: "GPS not ready",
|
|
48
|
-
51: "RTK not ready",
|
|
49
|
-
52: "IMU bias error",
|
|
50
|
-
53: "IMU data error",
|
|
51
|
-
60: "Boundary wire break",
|
|
52
|
-
61: "Boundary wire too long",
|
|
53
|
-
62: "Charging pile communication error",
|
|
54
|
-
63: "Charging pile overcurrent protection",
|
|
55
|
-
64: "Charging pile wire error",
|
|
56
|
-
65: "Recharge failure",
|
|
57
|
-
66: "Docking station return failure",
|
|
58
|
-
70: "WiFi config error",
|
|
59
|
-
71: "WiFi connection error",
|
|
60
|
-
80: "Firmware download error",
|
|
61
|
-
81: "Firmware upgrade error",
|
|
62
|
-
100: "Out of bounds",
|
|
63
|
-
101: "Unreachable mowing area",
|
|
64
|
-
102: "Started from forbidden zone",
|
|
65
|
-
103: "Started from virtual wall",
|
|
66
|
-
226: "Functional safety error",
|
|
67
|
-
227: "Ground app error",
|
|
68
|
-
228: "Ground base error",
|
|
69
|
-
229: "React error",
|
|
70
|
-
230: "Battery communication error",
|
|
71
|
-
231: "Drive motor stall",
|
|
72
|
-
232: "Drive motor overtemperature",
|
|
73
|
-
233: "Cutting motor stall protection",
|
|
74
|
-
234: "Mowing motor undervoltage",
|
|
75
|
-
235: "Mowing motor stuck",
|
|
76
|
-
236: "Lift up detected",
|
|
77
|
-
237: "Lift motor fault",
|
|
78
|
-
};
|
|
79
|
-
|
|
80
23
|
const RTK_STATE_OPTIONS = {
|
|
81
24
|
0: "not_ready",
|
|
82
25
|
1: "single",
|
|
@@ -194,12 +137,31 @@ function modelNameByCategory(categoryId) {
|
|
|
194
137
|
return MODEL_NAME_BY_CATEGORY[raw] || (raw ? `Anthbot ${raw}` : "Anthbot mower");
|
|
195
138
|
}
|
|
196
139
|
|
|
197
|
-
function
|
|
140
|
+
function eventCodeTranslationsFromCache(cacheOrPayload) {
|
|
141
|
+
const payload = cacheOrPayload?.payload && typeof cacheOrPayload.payload === "object"
|
|
142
|
+
? cacheOrPayload.payload
|
|
143
|
+
: cacheOrPayload;
|
|
144
|
+
return payload?.data && typeof payload.data === "object" && !Array.isArray(payload.data)
|
|
145
|
+
? payload.data
|
|
146
|
+
: {};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function errorDescription(data, cacheOrPayload = null, language = "English") {
|
|
198
150
|
const code = asInteger(data?.err_code);
|
|
199
151
|
if (code == null) {
|
|
200
152
|
return null;
|
|
201
153
|
}
|
|
202
|
-
|
|
154
|
+
const translations = eventCodeTranslationsFromCache(cacheOrPayload);
|
|
155
|
+
const byLanguage = translations[String(code)];
|
|
156
|
+
if (byLanguage && typeof byLanguage === "object" && !Array.isArray(byLanguage)) {
|
|
157
|
+
for (const candidateLanguage of [language, "English"]) {
|
|
158
|
+
const eventMessage = byLanguage[candidateLanguage]?.event_message;
|
|
159
|
+
if (typeof eventMessage === "string" && eventMessage.trim()) {
|
|
160
|
+
return eventMessage;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return `Unknown error (${code})`;
|
|
203
165
|
}
|
|
204
166
|
|
|
205
167
|
function rtkStateLabel(data) {
|
|
@@ -423,6 +385,58 @@ class AnthbotCloudApiClient {
|
|
|
423
385
|
return areaDefinition;
|
|
424
386
|
}
|
|
425
387
|
|
|
388
|
+
async getEventCodeVersion() {
|
|
389
|
+
this.requireToken();
|
|
390
|
+
const response = await this.http.get(`https://${this.host}/api/v1/message/code/version`, {
|
|
391
|
+
headers: this.authHeaders,
|
|
392
|
+
});
|
|
393
|
+
const payload = response.data;
|
|
394
|
+
if (response.status !== 200) {
|
|
395
|
+
throw new AnthbotGenieError(`Event code version failed (${response.status}): ${String(payload).slice(0, 300)}`);
|
|
396
|
+
}
|
|
397
|
+
if (!payload || typeof payload !== "object") {
|
|
398
|
+
throw new AnthbotGenieError("Invalid event code version payload type");
|
|
399
|
+
}
|
|
400
|
+
if (payload.code !== 0) {
|
|
401
|
+
throw new AnthbotGenieError(`Event code version returned code=${JSON.stringify(payload.code)}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const data = payload.data;
|
|
405
|
+
const version = typeof data === "object" && data !== null
|
|
406
|
+
? asInteger(data.version ?? data.event_code_version ?? data.code_version)
|
|
407
|
+
: asInteger(data);
|
|
408
|
+
if (version == null) {
|
|
409
|
+
throw new AnthbotGenieError("Event code version payload missing version");
|
|
410
|
+
}
|
|
411
|
+
return version;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async getEventCodeTranslations(version) {
|
|
415
|
+
this.requireToken();
|
|
416
|
+
const response = await this.http.post(`https://${this.host}/api/v1/message/code/translate`, {
|
|
417
|
+
version,
|
|
418
|
+
}, {
|
|
419
|
+
headers: {
|
|
420
|
+
...this.authHeaders,
|
|
421
|
+
"content-type": "application/json",
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
const payload = response.data;
|
|
425
|
+
if (response.status !== 200) {
|
|
426
|
+
throw new AnthbotGenieError(`Event code translations failed (${response.status}): ${String(payload).slice(0, 300)}`);
|
|
427
|
+
}
|
|
428
|
+
if (!payload || typeof payload !== "object") {
|
|
429
|
+
throw new AnthbotGenieError("Invalid event code translations payload type");
|
|
430
|
+
}
|
|
431
|
+
if (payload.code !== 0) {
|
|
432
|
+
throw new AnthbotGenieError(`Event code translations returned code=${JSON.stringify(payload.code)}`);
|
|
433
|
+
}
|
|
434
|
+
if (!payload.data || typeof payload.data !== "object" || Array.isArray(payload.data)) {
|
|
435
|
+
throw new AnthbotGenieError("Event code translations payload missing data object");
|
|
436
|
+
}
|
|
437
|
+
return payload;
|
|
438
|
+
}
|
|
439
|
+
|
|
426
440
|
async getDevicePresignedRegion(serialNumber) {
|
|
427
441
|
this.requireToken();
|
|
428
442
|
const response = await this.http.get(`https://${this.host}/api/v1/device/v2/presigned_url`, {
|
package/main.js
CHANGED
|
@@ -198,6 +198,7 @@ const DEVICE_STATE_DEFINITIONS = {
|
|
|
198
198
|
"zones.autoList": { type: "string", role: "json", read: true, write: false, name: t("Auto zones", "Automatische Zonen") },
|
|
199
199
|
"raw.shadow.property": { type: "string", role: "json", read: true, write: false, name: t("Raw property shadow", "Rohdaten Property Shadow") },
|
|
200
200
|
"raw.shadow.service": { type: "string", role: "json", read: true, write: false, name: t("Raw service shadow", "Rohdaten Service Shadow") },
|
|
201
|
+
"raw.shadow.event-code": { type: "string", role: "json", read: true, write: false, name: t("Raw event code translations", "Rohdaten Ereigniscode-Übersetzungen") },
|
|
201
202
|
"raw.areaDefinition": { type: "string", role: "json", read: true, write: false, name: t("Raw area definition", "Rohdaten Flächendefinition") },
|
|
202
203
|
};
|
|
203
204
|
|
|
@@ -246,6 +247,8 @@ class AnthbotGenieAdapter extends utils.Adapter {
|
|
|
246
247
|
this.cloudClient = null;
|
|
247
248
|
this.authToken = null;
|
|
248
249
|
this.deviceContexts = new Map();
|
|
250
|
+
this.eventCodeCache = null;
|
|
251
|
+
this.eventCodeCacheInitialized = false;
|
|
249
252
|
this.pollTimer = null;
|
|
250
253
|
this.refreshInFlight = null;
|
|
251
254
|
this.unloaded = false;
|
|
@@ -309,7 +312,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
|
|
|
309
312
|
if (this.pollTimer) {
|
|
310
313
|
this.clearTimeout(this.pollTimer);
|
|
311
314
|
}
|
|
312
|
-
const intervalSeconds = Math.max(10, Number(this.config.pollInterval) ||
|
|
315
|
+
const intervalSeconds = Math.max(10, Number(this.config.pollInterval) || 60);
|
|
313
316
|
this.pollTimer = this.setTimeout(async () => {
|
|
314
317
|
this.pollTimer = null;
|
|
315
318
|
try {
|
|
@@ -342,6 +345,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
|
|
|
342
345
|
try {
|
|
343
346
|
await this.ensureSession(forceLogin);
|
|
344
347
|
await this.discoverDevices(forceLogin);
|
|
348
|
+
await this.ensureEventCodeCache();
|
|
345
349
|
for (const context of this.deviceContexts.values()) {
|
|
346
350
|
try {
|
|
347
351
|
await this.refreshDevice(context);
|
|
@@ -361,6 +365,92 @@ class AnthbotGenieAdapter extends utils.Adapter {
|
|
|
361
365
|
await this.setStateAsync("info.connection", successful > 0, true);
|
|
362
366
|
}
|
|
363
367
|
|
|
368
|
+
async ensureEventCodeCache() {
|
|
369
|
+
if (this.eventCodeCacheInitialized) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
this.eventCodeCacheInitialized = true;
|
|
373
|
+
|
|
374
|
+
const cached = await this.readStoredEventCodeCache();
|
|
375
|
+
this.eventCodeCache = cached;
|
|
376
|
+
|
|
377
|
+
let cloudVersion = null;
|
|
378
|
+
try {
|
|
379
|
+
cloudVersion = await this.cloudClient.getEventCodeVersion();
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (cached) {
|
|
382
|
+
this.log.warn(`Failed to fetch event code version, using cached translations: ${error.message}`);
|
|
383
|
+
await this.writeEventCodeCacheToDevices(cached);
|
|
384
|
+
} else {
|
|
385
|
+
this.log.warn(`Failed to fetch event code version and no cached translations are available: ${error.message}`);
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (cached && asInteger(cached.version) === cloudVersion) {
|
|
391
|
+
this.eventCodeCache = cached;
|
|
392
|
+
await this.writeEventCodeCacheToDevices(cached);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
const payload = await this.cloudClient.getEventCodeTranslations(cloudVersion);
|
|
398
|
+
this.eventCodeCache = {
|
|
399
|
+
version: cloudVersion,
|
|
400
|
+
fetchedAt: new Date().toISOString(),
|
|
401
|
+
payload,
|
|
402
|
+
};
|
|
403
|
+
await this.writeEventCodeCacheToDevices(this.eventCodeCache);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
if (cached) {
|
|
406
|
+
this.log.warn(`Failed to fetch event code translations, using cached translations: ${error.message}`);
|
|
407
|
+
this.eventCodeCache = cached;
|
|
408
|
+
await this.writeEventCodeCacheToDevices(cached);
|
|
409
|
+
} else {
|
|
410
|
+
this.log.warn(`Failed to fetch event code translations and no cached translations are available: ${error.message}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async readStoredEventCodeCache() {
|
|
416
|
+
for (const context of this.deviceContexts.values()) {
|
|
417
|
+
const serial = context.device.serialNumber;
|
|
418
|
+
try {
|
|
419
|
+
const state = await this.getStateAsync(`${serial}.raw.shadow.event-code`);
|
|
420
|
+
const raw = typeof state?.val === "string" ? state.val : "";
|
|
421
|
+
if (!raw) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const parsed = JSON.parse(raw);
|
|
425
|
+
if (this.isValidEventCodeCache(parsed)) {
|
|
426
|
+
return parsed;
|
|
427
|
+
}
|
|
428
|
+
} catch (error) {
|
|
429
|
+
this.log.debug(`Stored event code cache could not be read for ${serial}: ${error.message}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
isValidEventCodeCache(cache) {
|
|
436
|
+
return Boolean(cache
|
|
437
|
+
&& typeof cache === "object"
|
|
438
|
+
&& asInteger(cache.version) != null
|
|
439
|
+
&& cache.payload
|
|
440
|
+
&& typeof cache.payload === "object"
|
|
441
|
+
&& !Array.isArray(cache.payload));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async writeEventCodeCacheToDevices(cache) {
|
|
445
|
+
if (!cache) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const value = JSON.stringify(cache);
|
|
449
|
+
for (const context of this.deviceContexts.values()) {
|
|
450
|
+
await this.setStateAsync(`${context.device.serialNumber}.raw.shadow.event-code`, { val: value, ack: true });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
364
454
|
async ensureSession(force = false) {
|
|
365
455
|
if (!this.cloudClient || force) {
|
|
366
456
|
this.cloudClient = new AnthbotCloudApiClient({
|
|
@@ -623,7 +713,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
|
|
|
623
713
|
"metrics.map.totalArea": typeof data.map_area === "number" ? data.map_area : null,
|
|
624
714
|
"metrics.map.status": asText(safeGet(data, "map_sta", "value")),
|
|
625
715
|
"metrics.error.code": asInteger(data.err_code),
|
|
626
|
-
"metrics.error.description": asText(errorDescription(data)),
|
|
716
|
+
"metrics.error.description": asText(errorDescription(data, this.eventCodeCache, this.config.errorDescriptionLanguage || "English")),
|
|
627
717
|
"metrics.error.active": isNonZero(data.err_code),
|
|
628
718
|
|
|
629
719
|
"location.gps.latitude": typeof safeGet(data, "anti_loss_pose", "posegps", "lat") === "number" ? safeGet(data, "anti_loss_pose", "posegps", "lat") : null,
|
|
@@ -701,6 +791,7 @@ class AnthbotGenieAdapter extends utils.Adapter {
|
|
|
701
791
|
|
|
702
792
|
"raw.shadow.property": JSON.stringify(context.lastReported || {}),
|
|
703
793
|
"raw.shadow.service": JSON.stringify(context.lastService || {}),
|
|
794
|
+
"raw.shadow.event-code": JSON.stringify(this.eventCodeCache || {}),
|
|
704
795
|
"raw.areaDefinition": JSON.stringify(context.areaDefinition || {}),
|
|
705
796
|
};
|
|
706
797
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.anthbot-genie",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
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",
|