iobroker.renault 0.0.24 → 0.0.25
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 +12 -0
- package/admin/i18n/de/translations.json +3 -1
- package/admin/i18n/en/translations.json +3 -1
- package/admin/i18n/es/translations.json +5 -1
- package/admin/i18n/fr/translations.json +5 -1
- package/admin/i18n/it/translations.json +5 -1
- package/admin/i18n/nl/translations.json +5 -1
- package/admin/i18n/pl/translations.json +5 -1
- package/admin/i18n/pt/translations.json +5 -1
- package/admin/i18n/ru/translations.json +5 -1
- package/admin/i18n/uk/translations.json +5 -1
- package/admin/i18n/zh-cn/translations.json +5 -1
- package/io-package.json +14 -27
- package/main.js +85 -50
- package/package.json +13 -16
- package/lib/descriptions.js +0 -1
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
**Tests:** 
|
|
11
11
|
|
|
12
|
+
**This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** For more details and for information how to disable the error reporting see [Sentry-Plugin Documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry)! Sentry reporting is used starting with js-controller 3.0.
|
|
13
|
+
|
|
12
14
|
## Renault / Dacia / Alpine adapter for ioBroker
|
|
13
15
|
|
|
14
16
|
This adapter connects ioBroker to the My Renault / My Dacia / My Alpine cloud and exposes vehicle status data (battery, charging, HVAC, mileage, ...) as well as remote commands (HVAC start, charging start/stop, force refresh) for compatible Renault, Dacia and Alpine models such as the Renault Zoe, Megane E-Tech, Kangoo E-Tech, the Dacia Spring and the Alpine A290.
|
|
@@ -48,6 +50,16 @@ ioBroker forum: <https://forum.iobroker.net/topic/48074/test-adapter-renault-v0-
|
|
|
48
50
|
### **WORK IN PROGRESS**
|
|
49
51
|
-->
|
|
50
52
|
|
|
53
|
+
### 0.0.25
|
|
54
|
+
|
|
55
|
+
- (typhosj) retry the connection with growing delay (5 to 60 minutes) when login or vehicle list fail at startup; a login rejected by the account service is not retried
|
|
56
|
+
- (typhosj) `info.connection` is true only after the account was found and turns false when the token refresh fails
|
|
57
|
+
- (typhosj) a temporary server error (5xx) on the first poll no longer disables that endpoint until restart
|
|
58
|
+
- (typhosj) fix crash for vehicles without vehicle details and for the refresh button before login
|
|
59
|
+
- (typhosj) no longer write password, session cookie, id token or account data into the log
|
|
60
|
+
- (typhosj) add missing admin translations, remove unused dependencies
|
|
61
|
+
- (typhosj) require Node.js 22 or newer, test with Node.js 26, update dependencies
|
|
62
|
+
|
|
51
63
|
### 0.0.24
|
|
52
64
|
|
|
53
65
|
- (TA2k) add Alpine support: brand selection (Alpine accounts use the same Renault Gigya/Kamereon tenant, only product/account type MYALPINE differs)
|
|
@@ -12,5 +12,7 @@
|
|
|
12
12
|
"API Key": "API-Schlüssel",
|
|
13
13
|
"API Key (leave empty for auto-detect)": "API-Schlüssel (leer lassen für automatische Erkennung)",
|
|
14
14
|
"If empty, the API key will be set automatically": "Wenn leer, wird der API-Schlüssel automatisch gesetzt",
|
|
15
|
-
"Click here for current API Key": "Hier klicken für aktuellen API-Schlüssel"
|
|
15
|
+
"Click here for current API Key": "Hier klicken für aktuellen API-Schlüssel",
|
|
16
|
+
"Charge history entries to keep": "Aufzubewahrende Einträge der Ladehistorie",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Anzahl der neuesten Einträge, die je Ladehistorie-Endpunkt behalten werden (0 = unbegrenzt)"
|
|
16
18
|
}
|
|
@@ -12,5 +12,7 @@
|
|
|
12
12
|
"API Key": "API Key",
|
|
13
13
|
"API Key (leave empty for auto-detect)": "API Key (leave empty for auto-detect)",
|
|
14
14
|
"If empty, the API key will be set automatically": "If empty, the API key will be set automatically",
|
|
15
|
-
"Click here for current API Key": "Click here for current API Key"
|
|
15
|
+
"Click here for current API Key": "Click here for current API Key",
|
|
16
|
+
"Charge history entries to keep": "Charge history entries to keep",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Number of most recent entries kept per charge-history endpoint (0 = unlimited)"
|
|
16
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "Correo electrónico de la app",
|
|
4
4
|
"App Password": "Contraseña de la app",
|
|
5
5
|
"Country": "País",
|
|
6
|
+
"Brand": "Marca",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia y Alpine usan cuentas y claves API diferentes",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "Código de país de dos letras, p. ej. de, fr, it, es",
|
|
7
9
|
"Polling": "Sondeo",
|
|
8
10
|
"Update interval in minutes": "Intervalo de actualización en minutos",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "Clave API",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "Clave API (déjelo vacío para detección automática)",
|
|
12
14
|
"If empty, the API key will be set automatically": "Si está vacío, la clave API se establecerá automáticamente",
|
|
13
|
-
"Click here for current API Key": "Haga clic aquí para la clave API actual"
|
|
15
|
+
"Click here for current API Key": "Haga clic aquí para la clave API actual",
|
|
16
|
+
"Charge history entries to keep": "Entradas del historial de carga a conservar",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Número de entradas más recientes que se conservan por endpoint del historial de carga (0 = ilimitado)"
|
|
14
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "Email de l'application",
|
|
4
4
|
"App Password": "Mot de passe de l'application",
|
|
5
5
|
"Country": "Pays",
|
|
6
|
+
"Brand": "Marque",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia et Alpine utilisent des comptes et des clés API différents",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "Code pays à deux lettres, par exemple de, fr, it, es",
|
|
7
9
|
"Polling": "Interrogation",
|
|
8
10
|
"Update interval in minutes": "Intervalle de mise à jour en minutes",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "Clé API",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "Clé API (laisser vide pour détection automatique)",
|
|
12
14
|
"If empty, the API key will be set automatically": "Si vide, la clé API sera définie automatiquement",
|
|
13
|
-
"Click here for current API Key": "Cliquez ici pour la clé API actuelle"
|
|
15
|
+
"Click here for current API Key": "Cliquez ici pour la clé API actuelle",
|
|
16
|
+
"Charge history entries to keep": "Entrées de l'historique de charge à conserver",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Nombre d'entrées les plus récentes conservées par point d'accès de l'historique de charge (0 = illimité)"
|
|
14
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "Email dell'app",
|
|
4
4
|
"App Password": "Password dell'app",
|
|
5
5
|
"Country": "Paese",
|
|
6
|
+
"Brand": "Marchio",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia e Alpine usano account e chiavi API diversi",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "Codice paese di due lettere, ad esempio de, fr, it, es",
|
|
7
9
|
"Polling": "Polling",
|
|
8
10
|
"Update interval in minutes": "Intervallo di aggiornamento in minuti",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "Chiave API",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "Chiave API (lascia vuoto per il rilevamento automatico)",
|
|
12
14
|
"If empty, the API key will be set automatically": "Se vuoto, la chiave API verrà impostata automaticamente",
|
|
13
|
-
"Click here for current API Key": "Fai clic qui per la chiave API attuale"
|
|
15
|
+
"Click here for current API Key": "Fai clic qui per la chiave API attuale",
|
|
16
|
+
"Charge history entries to keep": "Voci della cronologia di ricarica da conservare",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Numero di voci più recenti conservate per ogni endpoint della cronologia di ricarica (0 = illimitato)"
|
|
14
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "App E-mail",
|
|
4
4
|
"App Password": "App Wachtwoord",
|
|
5
5
|
"Country": "Land",
|
|
6
|
+
"Brand": "Merk",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia en Alpine gebruiken verschillende accounts en API-sleutels",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "Tweeletterige landcode, bijv. de, fr, it, es",
|
|
7
9
|
"Polling": "Polling",
|
|
8
10
|
"Update interval in minutes": "Update interval in minuten",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "API-sleutel",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "API-sleutel (laat leeg voor auto-detectie)",
|
|
12
14
|
"If empty, the API key will be set automatically": "Indien leeg, wordt de API-sleutel automatisch ingesteld",
|
|
13
|
-
"Click here for current API Key": "Klik hier voor de huidige API-sleutel"
|
|
15
|
+
"Click here for current API Key": "Klik hier voor de huidige API-sleutel",
|
|
16
|
+
"Charge history entries to keep": "Te bewaren items van de laadgeschiedenis",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Aantal meest recente items dat per laadgeschiedenis-endpoint wordt bewaard (0 = onbeperkt)"
|
|
14
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "Email aplikacji",
|
|
4
4
|
"App Password": "Hasło aplikacji",
|
|
5
5
|
"Country": "Kraj",
|
|
6
|
+
"Brand": "Marka",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia i Alpine używają różnych kont i kluczy API",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "Dwuliterowy kod kraju, np. de, fr, it, es",
|
|
7
9
|
"Polling": "Odpytywanie",
|
|
8
10
|
"Update interval in minutes": "Interwał aktualizacji w minutach",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "Klucz API",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "Klucz API (pozostaw puste, aby wykryć automatycznie)",
|
|
12
14
|
"If empty, the API key will be set automatically": "Jeśli puste, klucz API zostanie ustawiony automatycznie",
|
|
13
|
-
"Click here for current API Key": "Kliknij tutaj, aby uzyskać aktualny klucz API"
|
|
15
|
+
"Click here for current API Key": "Kliknij tutaj, aby uzyskać aktualny klucz API",
|
|
16
|
+
"Charge history entries to keep": "Liczba zachowywanych wpisów historii ładowania",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Liczba najnowszych wpisów zachowywanych dla każdego endpointu historii ładowania (0 = bez limitu)"
|
|
14
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "Email do aplicativo",
|
|
4
4
|
"App Password": "Senha do aplicativo",
|
|
5
5
|
"Country": "País",
|
|
6
|
+
"Brand": "Marca",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia e Alpine utilizam contas e chaves de API diferentes",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "Código de país de duas letras, por exemplo de, fr, it, es",
|
|
7
9
|
"Polling": "Sondagem",
|
|
8
10
|
"Update interval in minutes": "Intervalo de atualização em minutos",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "Chave API",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "Chave API (deixe em branco para detecção automática)",
|
|
12
14
|
"If empty, the API key will be set automatically": "Se vazio, a chave API será definida automaticamente",
|
|
13
|
-
"Click here for current API Key": "Clique aqui para a chave API atual"
|
|
15
|
+
"Click here for current API Key": "Clique aqui para a chave API atual",
|
|
16
|
+
"Charge history entries to keep": "Entradas do histórico de carregamento a manter",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Número de entradas mais recentes mantidas por endpoint do histórico de carregamento (0 = ilimitado)"
|
|
14
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "Электронная почта приложения",
|
|
4
4
|
"App Password": "Пароль приложения",
|
|
5
5
|
"Country": "Страна",
|
|
6
|
+
"Brand": "Марка",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia и Alpine используют разные учётные записи и API-ключи",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "Двухбуквенный код страны, например de, fr, it, es",
|
|
7
9
|
"Polling": "Опрос",
|
|
8
10
|
"Update interval in minutes": "Интервал обновления в минутах",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "API ключ",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "API ключ (оставьте пустым для автоопределения)",
|
|
12
14
|
"If empty, the API key will be set automatically": "Если пусто, API ключ будет установлен автоматически",
|
|
13
|
-
"Click here for current API Key": "Нажмите здесь для получения текущего API ключа"
|
|
15
|
+
"Click here for current API Key": "Нажмите здесь для получения текущего API ключа",
|
|
16
|
+
"Charge history entries to keep": "Сохраняемые записи истории зарядки",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Количество последних записей, сохраняемых для каждого эндпоинта истории зарядки (0 = без ограничений)"
|
|
14
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "Електронна пошта застосунку",
|
|
4
4
|
"App Password": "Пароль застосунку",
|
|
5
5
|
"Country": "Країна",
|
|
6
|
+
"Brand": "Марка",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia та Alpine використовують різні облікові записи та API-ключі",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "Дволітерний код країни, наприклад de, fr, it, es",
|
|
7
9
|
"Polling": "Опитування",
|
|
8
10
|
"Update interval in minutes": "Інтервал оновлення в хвилинах",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "API ключ",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "API ключ (залиште порожнім для автовизначення)",
|
|
12
14
|
"If empty, the API key will be set automatically": "Якщо порожньо, API ключ буде встановлено автоматично",
|
|
13
|
-
"Click here for current API Key": "Натисніть тут для отримання поточного API ключа"
|
|
15
|
+
"Click here for current API Key": "Натисніть тут для отримання поточного API ключа",
|
|
16
|
+
"Charge history entries to keep": "Записи історії заряджання, що зберігаються",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "Кількість останніх записів, що зберігаються для кожного ендпоінта історії заряджання (0 = без обмежень)"
|
|
14
18
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"App Email": "应用邮箱",
|
|
4
4
|
"App Password": "应用密码",
|
|
5
5
|
"Country": "国家",
|
|
6
|
+
"Brand": "品牌",
|
|
7
|
+
"Renault/Dacia and Alpine use different accounts and API keys": "Renault/Dacia 和 Alpine 使用不同的账户和 API 密钥",
|
|
6
8
|
"Two-letter country code, e.g. de, fr, it, es": "两位字母的国家代码,例如 de, fr, it, es",
|
|
7
9
|
"Polling": "轮询",
|
|
8
10
|
"Update interval in minutes": "更新间隔(分钟)",
|
|
@@ -10,5 +12,7 @@
|
|
|
10
12
|
"API Key": "API 密钥",
|
|
11
13
|
"API Key (leave empty for auto-detect)": "API 密钥(留空自动检测)",
|
|
12
14
|
"If empty, the API key will be set automatically": "如果为空,API 密钥将自动设置",
|
|
13
|
-
"Click here for current API Key": "点击此处获取当前 API 密钥"
|
|
15
|
+
"Click here for current API Key": "点击此处获取当前 API 密钥",
|
|
16
|
+
"Charge history entries to keep": "保留的充电历史记录条数",
|
|
17
|
+
"Number of most recent entries kept per charge-history endpoint (0 = unlimited)": "每个充电历史接口保留的最新记录条数(0 = 不限制)"
|
|
14
18
|
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "renault",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.25",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.0.25": {
|
|
7
|
+
"en": "more robust startup with retry, truthful connection state, no secrets in the log, require Node.js 22+",
|
|
8
|
+
"de": "robusterer Start mit Wiederholung, ehrlicher Verbindungsstatus, keine Geheimnisse im Log, benötigt Node.js 22+",
|
|
9
|
+
"ru": "более надёжный запуск с повтором, честный статус соединения, без секретов в логе, требуется Node.js 22+",
|
|
10
|
+
"pt": "arranque mais robusto com nova tentativa, estado de ligação fiável, sem segredos no registo, requer Node.js 22+",
|
|
11
|
+
"nl": "robuustere start met herhaling, eerlijke verbindingsstatus, geen geheimen in het logboek, vereist Node.js 22+",
|
|
12
|
+
"fr": "démarrage plus robuste avec nouvelle tentative, état de connexion fiable, aucun secret dans le journal, nécessite Node.js 22+",
|
|
13
|
+
"it": "avvio più robusto con ripetizione, stato di connessione affidabile, nessun segreto nel log, richiede Node.js 22+",
|
|
14
|
+
"es": "inicio más robusto con reintento, estado de conexión fiable, sin secretos en el registro, requiere Node.js 22+",
|
|
15
|
+
"pl": "solidniejszy start z ponawianiem, prawdziwy status połączenia, bez sekretów w logu, wymaga Node.js 22+",
|
|
16
|
+
"uk": "надійніший запуск із повтором, чесний статус з'єднання, без секретів у журналі, потрібен Node.js 22+",
|
|
17
|
+
"zh-cn": "启动更稳健并带重试、连接状态真实、日志中不含密钥、需要 Node.js 22+"
|
|
18
|
+
},
|
|
6
19
|
"0.0.24": {
|
|
7
20
|
"en": "add Alpine support (select brand Alpine in the settings)",
|
|
8
21
|
"de": "Alpine-Unterstützung hinzugefügt (Marke Alpine in den Einstellungen wählen)",
|
|
@@ -80,32 +93,6 @@
|
|
|
80
93
|
"pl": "Naprawiono start ładowania i zmniejszono logi błędów",
|
|
81
94
|
"uk": "Виправлено старт зарядки та зменшено журнали помилок",
|
|
82
95
|
"zh-cn": "修复充电启动并减少错误日志"
|
|
83
|
-
},
|
|
84
|
-
"0.0.14": {
|
|
85
|
-
"en": "add charging stop",
|
|
86
|
-
"de": "Ladestopp hinzugefügt",
|
|
87
|
-
"ru": "Добавлена остановка зарядки",
|
|
88
|
-
"pt": "Adicionada paragem de carga",
|
|
89
|
-
"nl": "Stoppen van het laden toegevoegd",
|
|
90
|
-
"fr": "Ajout de l'arrêt de la charge",
|
|
91
|
-
"it": "Aggiunto stop della carica",
|
|
92
|
-
"es": "Añadida parada de carga",
|
|
93
|
-
"pl": "Dodano zatrzymanie ładowania",
|
|
94
|
-
"uk": "Додано зупинку зарядки",
|
|
95
|
-
"zh-cn": "添加充电停止"
|
|
96
|
-
},
|
|
97
|
-
"0.0.13": {
|
|
98
|
-
"en": "add remote object for force refresh",
|
|
99
|
-
"de": "Remote-Objekt für erzwungene Aktualisierung hinzugefügt",
|
|
100
|
-
"ru": "Добавлен удалённый объект для принудительного обновления",
|
|
101
|
-
"pt": "Adicionado objeto remoto para forçar atualização",
|
|
102
|
-
"nl": "Remote-object voor geforceerde verversing toegevoegd",
|
|
103
|
-
"fr": "Ajout d'un objet distant pour le rafraîchissement forcé",
|
|
104
|
-
"it": "Aggiunto oggetto remoto per il refresh forzato",
|
|
105
|
-
"es": "Añadido objeto remoto para actualización forzada",
|
|
106
|
-
"pl": "Dodano obiekt zdalny do wymuszonego odświeżenia",
|
|
107
|
-
"uk": "Додано віддалений об'єкт для примусового оновлення",
|
|
108
|
-
"zh-cn": "添加用于强制刷新的 remote 对象"
|
|
109
96
|
}
|
|
110
97
|
},
|
|
111
98
|
"titleLang": {
|
package/main.js
CHANGED
|
@@ -29,6 +29,14 @@ class Renault extends utils.Adapter {
|
|
|
29
29
|
this.firstUpdate = true;
|
|
30
30
|
this.requestClient = axios.create();
|
|
31
31
|
this.userAgent = 'okhttp/5.3.0';
|
|
32
|
+
/** @type {string} */
|
|
33
|
+
this.brand = 'renault';
|
|
34
|
+
/** @type {string[]} */
|
|
35
|
+
this.accountTypes = [];
|
|
36
|
+
this.updateInterval = null;
|
|
37
|
+
this.refreshTokenInterval = null;
|
|
38
|
+
this.startAttempt = 0;
|
|
39
|
+
this.loginRejected = false;
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
/** APK rI2.smali (WiredHeaderAppVersionInterceptor): build={brand}-android-{version};trId={uuid} on wired Kamereon host */
|
|
@@ -38,7 +46,7 @@ class Renault extends utils.Adapter {
|
|
|
38
46
|
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
39
47
|
return v.toString(16);
|
|
40
48
|
});
|
|
41
|
-
const brand = this.brand
|
|
49
|
+
const brand = this.brand;
|
|
42
50
|
const version = brand === 'alpine' ? '6.10.1' : '6.11.2';
|
|
43
51
|
return 'build=' + brand + '-android-' + version + ';trId=' + uuid;
|
|
44
52
|
}
|
|
@@ -53,7 +61,6 @@ class Renault extends utils.Adapter {
|
|
|
53
61
|
this.log.info('Set interval to minimum 0.5');
|
|
54
62
|
this.config.interval = 0.5;
|
|
55
63
|
}
|
|
56
|
-
this.updateInterval = null;
|
|
57
64
|
this.reLoginTimeout = null;
|
|
58
65
|
this.refreshTokenTimeout = null;
|
|
59
66
|
this.country = this.config.country || 'de';
|
|
@@ -76,17 +83,15 @@ class Renault extends utils.Adapter {
|
|
|
76
83
|
})
|
|
77
84
|
|
|
78
85
|
.then((res) => {
|
|
79
|
-
this.log.debug(JSON.stringify(res.data));
|
|
80
|
-
|
|
81
86
|
if (res.data.split('KAMEREON_APIKEY = "')[2] && res.data.split('KAMEREON_APIKEY = "')[2].split('"')[0]) {
|
|
82
87
|
this.apiKeyUpdate = res.data.split('KAMEREON_APIKEY = "')[2].split('"')[0];
|
|
83
88
|
}
|
|
84
89
|
})
|
|
85
90
|
.catch((error) => {
|
|
86
|
-
this.log.debug(error);
|
|
91
|
+
this.log.debug('API key lookup failed: ' + error);
|
|
87
92
|
});
|
|
88
93
|
} catch (error) {
|
|
89
|
-
this.log.debug(error);
|
|
94
|
+
this.log.debug('API key lookup failed: ' + error);
|
|
90
95
|
}
|
|
91
96
|
if (this.config.apiKeyUpdate) {
|
|
92
97
|
this.apiKeyUpdate = this.config.apiKeyUpdate;
|
|
@@ -94,10 +99,16 @@ class Renault extends utils.Adapter {
|
|
|
94
99
|
|
|
95
100
|
this.subscribeStates('*');
|
|
96
101
|
|
|
97
|
-
await this.
|
|
102
|
+
await this.connectAndPoll();
|
|
103
|
+
}
|
|
98
104
|
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Log in, load the vehicles and start polling. A network or server failure is retried with
|
|
107
|
+
* growing delay (5 min doubling up to 60 min). A login the account service rejected is not
|
|
108
|
+
* retried, so wrong credentials cannot lock the account.
|
|
109
|
+
*/
|
|
110
|
+
async connectAndPoll() {
|
|
111
|
+
if ((await this.login()) && (await this.getDeviceList())) {
|
|
101
112
|
await this.migrateChargeHistoryV1();
|
|
102
113
|
await this.updateDevices();
|
|
103
114
|
this.updateInterval = setInterval(
|
|
@@ -109,9 +120,33 @@ class Renault extends utils.Adapter {
|
|
|
109
120
|
this.refreshTokenInterval = setInterval(() => {
|
|
110
121
|
this.refreshToken();
|
|
111
122
|
}, 3500 * 1000);
|
|
123
|
+
return;
|
|
112
124
|
}
|
|
125
|
+
if (this.loginRejected) {
|
|
126
|
+
this.log.error('Login rejected. Check email and password in the adapter settings, then restart the instance.');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const delayMinutes = Math.min(5 * 2 ** this.startAttempt, 60);
|
|
130
|
+
this.startAttempt++;
|
|
131
|
+
this.log.warn('Connection to the Renault cloud failed. Next attempt in ' + delayMinutes + ' minutes');
|
|
132
|
+
this.setTimeout(
|
|
133
|
+
() => this.connectAndPoll().catch((error) => this.log.error('Connection attempt failed: ' + error)),
|
|
134
|
+
delayMinutes * 60 * 1000,
|
|
135
|
+
);
|
|
113
136
|
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Log in and look up the account. Sets info.connection accordingly.
|
|
140
|
+
*
|
|
141
|
+
* @returns {Promise<boolean>} true when session, id token and account are available
|
|
142
|
+
*/
|
|
114
143
|
async login() {
|
|
144
|
+
const ok = await this.loginSteps();
|
|
145
|
+
this.setState('info.connection', ok, true);
|
|
146
|
+
return ok;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async loginSteps() {
|
|
115
150
|
this.session_data = await this.requestClient({
|
|
116
151
|
method: 'post',
|
|
117
152
|
url: 'https://accounts.eu1.gigya.com/accounts.login',
|
|
@@ -131,23 +166,24 @@ class Renault extends utils.Adapter {
|
|
|
131
166
|
}),
|
|
132
167
|
})
|
|
133
168
|
.then((res) => {
|
|
134
|
-
this.log.debug(JSON.stringify(res.data));
|
|
135
169
|
if (res.data.errorMessage) {
|
|
170
|
+
this.loginRejected = true;
|
|
136
171
|
this.log.error(JSON.stringify(res.data));
|
|
137
172
|
return;
|
|
138
173
|
}
|
|
139
174
|
return res.data.sessionInfo;
|
|
140
175
|
})
|
|
141
176
|
.catch((error) => {
|
|
142
|
-
this.log.error(error);
|
|
177
|
+
this.log.error('Login failed: ' + error);
|
|
143
178
|
if (error.response) {
|
|
144
179
|
this.log.error(JSON.stringify(error.response.data));
|
|
145
180
|
}
|
|
146
181
|
});
|
|
147
182
|
if (!this.session_data) {
|
|
148
183
|
this.log.error('No session found for this account. Please login in the app. Maybe a new password is needed.');
|
|
149
|
-
return;
|
|
184
|
+
return false;
|
|
150
185
|
}
|
|
186
|
+
this.session = {};
|
|
151
187
|
await this.requestClient({
|
|
152
188
|
method: 'post',
|
|
153
189
|
url: 'https://accounts.eu1.gigya.com/accounts.getJWT',
|
|
@@ -168,17 +204,18 @@ class Renault extends utils.Adapter {
|
|
|
168
204
|
}),
|
|
169
205
|
})
|
|
170
206
|
.then((res) => {
|
|
171
|
-
this.log.debug(JSON.stringify(res.data));
|
|
172
207
|
this.session = res.data;
|
|
173
|
-
this.setState('info.connection', true, true);
|
|
174
208
|
})
|
|
175
209
|
.catch((error) => {
|
|
176
|
-
this.log.error(error);
|
|
210
|
+
this.log.error('Getting the id token failed: ' + error);
|
|
177
211
|
if (error.response) {
|
|
178
212
|
this.log.error(JSON.stringify(error.response.data));
|
|
179
213
|
}
|
|
180
214
|
});
|
|
181
|
-
|
|
215
|
+
if (!this.session.id_token) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
return await this.requestClient({
|
|
182
219
|
method: 'post',
|
|
183
220
|
url:
|
|
184
221
|
'https://apis.renault.com/myr/api/v1/connection?&country=DE&product=' +
|
|
@@ -195,34 +232,41 @@ class Renault extends utils.Adapter {
|
|
|
195
232
|
},
|
|
196
233
|
})
|
|
197
234
|
.then((res) => {
|
|
198
|
-
this.log.debug(JSON.stringify(res.data));
|
|
199
|
-
|
|
200
235
|
const accountTypes = this.accountTypes;
|
|
201
236
|
const filteredAccounts = res.data.currentUser.accounts.filter(function (el) {
|
|
202
237
|
return accountTypes.includes(el.accountType) && el.accountStatus === 'ACTIVE';
|
|
203
238
|
});
|
|
204
239
|
if (filteredAccounts.length === 0) {
|
|
205
240
|
this.log.error('No Account found');
|
|
206
|
-
this.log.error(
|
|
207
|
-
|
|
208
|
-
|
|
241
|
+
this.log.error(
|
|
242
|
+
'Accounts of this login: ' +
|
|
243
|
+
JSON.stringify(res.data.currentUser.accounts.map((el) => el.accountType + ' ' + el.accountStatus)),
|
|
244
|
+
);
|
|
245
|
+
return false;
|
|
209
246
|
}
|
|
210
247
|
|
|
211
248
|
this.account = filteredAccounts[0];
|
|
249
|
+
return true;
|
|
212
250
|
})
|
|
213
251
|
.catch((error) => {
|
|
214
|
-
this.log.error('Error while getting account');
|
|
215
|
-
this.log.error(error);
|
|
252
|
+
this.log.error('Error while getting account: ' + error);
|
|
216
253
|
if (error.response) {
|
|
217
254
|
this.log.error(JSON.stringify(error.response.data));
|
|
218
255
|
if (error.response.data && JSON.stringify(error.response.data).indexOf('apikey') !== -1) {
|
|
219
256
|
this.log.error('Wrong API Key. Please update API Key in adapter settings');
|
|
220
257
|
}
|
|
221
258
|
}
|
|
259
|
+
return false;
|
|
222
260
|
});
|
|
223
261
|
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Load the vehicles of the account and create their objects.
|
|
265
|
+
*
|
|
266
|
+
* @returns {Promise<boolean>} true when the vehicle list was loaded
|
|
267
|
+
*/
|
|
224
268
|
async getDeviceList() {
|
|
225
|
-
await this.requestClient({
|
|
269
|
+
return await this.requestClient({
|
|
226
270
|
method: 'get',
|
|
227
271
|
url:
|
|
228
272
|
'https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/' +
|
|
@@ -243,10 +287,11 @@ class Renault extends utils.Adapter {
|
|
|
243
287
|
.then(async (res) => {
|
|
244
288
|
this.log.debug(JSON.stringify(res.data));
|
|
245
289
|
|
|
290
|
+
this.deviceArray = [];
|
|
246
291
|
for (const device of res.data.vehicleLinks) {
|
|
247
292
|
this.deviceArray.push(device.vin);
|
|
248
293
|
let name = device.vehicleDetails?.modelSCR || device.brand;
|
|
249
|
-
if (device.vehicleDetails
|
|
294
|
+
if (device.vehicleDetails?.model?.label) {
|
|
250
295
|
name += device.vehicleDetails.model.label;
|
|
251
296
|
}
|
|
252
297
|
|
|
@@ -302,11 +347,12 @@ class Renault extends utils.Adapter {
|
|
|
302
347
|
delete device.mileage;
|
|
303
348
|
this.json2iob.parse(device.vin + '.general', device);
|
|
304
349
|
}
|
|
350
|
+
return true;
|
|
305
351
|
})
|
|
306
352
|
.catch((error) => {
|
|
307
|
-
this.log.error('Error while getting vehicle list');
|
|
308
|
-
this.log.error(error);
|
|
353
|
+
this.log.error('Error while getting vehicle list: ' + error);
|
|
309
354
|
error.response && this.log.error(JSON.stringify(error.response.data));
|
|
355
|
+
return false;
|
|
310
356
|
});
|
|
311
357
|
}
|
|
312
358
|
|
|
@@ -336,7 +382,7 @@ class Renault extends utils.Adapter {
|
|
|
336
382
|
}
|
|
337
383
|
|
|
338
384
|
async updateDevices() {
|
|
339
|
-
if (!this.account
|
|
385
|
+
if (!this.account?.accountId) {
|
|
340
386
|
this.log.error('No accountId found');
|
|
341
387
|
return;
|
|
342
388
|
}
|
|
@@ -546,20 +592,13 @@ class Renault extends utils.Adapter {
|
|
|
546
592
|
return;
|
|
547
593
|
}
|
|
548
594
|
if (this.firstUpdate) {
|
|
549
|
-
if (
|
|
550
|
-
error.response.status === 403 ||
|
|
551
|
-
error.response.status === 404 ||
|
|
552
|
-
error.response.status === 500 ||
|
|
553
|
-
error.response.status === 501 ||
|
|
554
|
-
error.response.status === 502 ||
|
|
555
|
-
error.response.status === 400
|
|
556
|
-
) {
|
|
595
|
+
if (error.response.status === 400 || error.response.status === 403 || error.response.status === 404) {
|
|
557
596
|
if (!this.ignoreState[vin]) {
|
|
558
597
|
this.ignoreState[vin] = [];
|
|
559
598
|
}
|
|
560
599
|
this.ignoreState[vin].push(element.path);
|
|
561
600
|
this.log.info('Feature not found for ' + vin + '. Ignore ' + element.path + ' for updates.');
|
|
562
|
-
this.log.debug(error);
|
|
601
|
+
this.log.debug(String(error));
|
|
563
602
|
error.response && this.log.debug(JSON.stringify(error.response.data));
|
|
564
603
|
return;
|
|
565
604
|
}
|
|
@@ -570,7 +609,7 @@ class Renault extends utils.Adapter {
|
|
|
570
609
|
return;
|
|
571
610
|
}
|
|
572
611
|
this.log.error(url);
|
|
573
|
-
this.log.error(error);
|
|
612
|
+
this.log.error(String(error));
|
|
574
613
|
error.response && this.log.error(JSON.stringify(error.response.data));
|
|
575
614
|
});
|
|
576
615
|
}
|
|
@@ -603,13 +642,12 @@ class Renault extends utils.Adapter {
|
|
|
603
642
|
}),
|
|
604
643
|
})
|
|
605
644
|
.then((res) => {
|
|
606
|
-
this.log.debug(JSON.stringify(res.data));
|
|
607
645
|
this.session = res.data;
|
|
608
646
|
this.setState('info.connection', true, true);
|
|
609
647
|
})
|
|
610
648
|
.catch((error) => {
|
|
611
|
-
this.
|
|
612
|
-
this.log.error(error);
|
|
649
|
+
this.setState('info.connection', false, true);
|
|
650
|
+
this.log.error('refresh token failed: ' + error);
|
|
613
651
|
error.response && this.log.error(JSON.stringify(error.response.data));
|
|
614
652
|
this.log.error('Start relogin in 1min');
|
|
615
653
|
this.reLoginTimeout = setTimeout(
|
|
@@ -620,9 +658,6 @@ class Renault extends utils.Adapter {
|
|
|
620
658
|
);
|
|
621
659
|
});
|
|
622
660
|
}
|
|
623
|
-
sleep(ms) {
|
|
624
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
625
|
-
}
|
|
626
661
|
toCamelCase(string) {
|
|
627
662
|
if (!string) {
|
|
628
663
|
return;
|
|
@@ -645,7 +680,7 @@ class Renault extends utils.Adapter {
|
|
|
645
680
|
this.reLoginTimeout && clearTimeout(this.reLoginTimeout);
|
|
646
681
|
this.refreshTokenTimeout && clearTimeout(this.refreshTokenTimeout);
|
|
647
682
|
this.updateInterval && clearInterval(this.updateInterval);
|
|
648
|
-
clearInterval(this.refreshTokenInterval);
|
|
683
|
+
this.refreshTokenInterval && clearInterval(this.refreshTokenInterval);
|
|
649
684
|
callback();
|
|
650
685
|
} catch (e) {
|
|
651
686
|
this.log.error('Error onUnload: ' + e);
|
|
@@ -666,15 +701,15 @@ class Renault extends utils.Adapter {
|
|
|
666
701
|
if (path === 'hvac-temperature') {
|
|
667
702
|
return;
|
|
668
703
|
}
|
|
704
|
+
if (!this.account) {
|
|
705
|
+
this.log.error('No account found');
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
669
708
|
if (path === 'refresh') {
|
|
670
709
|
this.log.info('Force refresh');
|
|
671
710
|
this.updateDevices();
|
|
672
711
|
return;
|
|
673
712
|
}
|
|
674
|
-
if (!this.account) {
|
|
675
|
-
this.log.error('No account found');
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
713
|
const command = path.split('/')[1];
|
|
679
714
|
let action = state.val ? 'start' : 'cancel';
|
|
680
715
|
let midPart = 'kca/car-adapter/v1/cars/';
|
|
@@ -722,7 +757,7 @@ class Renault extends utils.Adapter {
|
|
|
722
757
|
return res.data;
|
|
723
758
|
})
|
|
724
759
|
.catch((error) => {
|
|
725
|
-
this.log.error(error);
|
|
760
|
+
this.log.error('Command ' + path + ' failed: ' + error);
|
|
726
761
|
if (error.response) {
|
|
727
762
|
this.log.error(JSON.stringify(error.response.data));
|
|
728
763
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.renault",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Adapter for Renault and
|
|
3
|
+
"version": "0.0.25",
|
|
4
|
+
"description": "Adapter for Renault, Dacia and Alpine",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TA2k",
|
|
7
7
|
"email": "tombox2020@gmail.com"
|
|
@@ -26,32 +26,29 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@iobroker/adapter-core": "^3.4.3",
|
|
29
|
-
"axios": "^1.
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"qs": "^6.16.0"
|
|
29
|
+
"axios": "^1.16.1",
|
|
30
|
+
"json2iob": "^2.6.22",
|
|
31
|
+
"qs": "^6.15.2"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"@alcalzone/release-script": "^5.2.1",
|
|
36
35
|
"@alcalzone/release-script-plugin-iobroker": "^5.2.0",
|
|
37
36
|
"@alcalzone/release-script-plugin-license": "^5.2.2",
|
|
38
37
|
"@alcalzone/release-script-plugin-manual-review": "^5.2.0",
|
|
39
|
-
"@eslint/eslintrc": "^3.3.
|
|
38
|
+
"@eslint/eslintrc": "^3.3.5",
|
|
40
39
|
"@eslint/js": "^10.0.1",
|
|
41
|
-
"@iobroker/testing": "^
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
44
|
-
"@types/node": "^25.9.6",
|
|
45
|
-
"eslint": "^10.10.0",
|
|
40
|
+
"@iobroker/testing": "^6.2.2",
|
|
41
|
+
"@types/node": "^22.20.3",
|
|
42
|
+
"eslint": "^10.4.0",
|
|
46
43
|
"eslint-config-prettier": "^10.1.8",
|
|
47
|
-
"eslint-plugin-prettier": "^5.5.
|
|
48
|
-
"globals": "^17.
|
|
49
|
-
"prettier": "^3.
|
|
44
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
45
|
+
"globals": "^17.6.0",
|
|
46
|
+
"prettier": "^3.8.3",
|
|
50
47
|
"typescript": "~6.0.3"
|
|
51
48
|
},
|
|
52
49
|
"main": "main.js",
|
|
53
50
|
"engines": {
|
|
54
|
-
"node": ">=
|
|
51
|
+
"node": ">=22"
|
|
55
52
|
},
|
|
56
53
|
"files": [
|
|
57
54
|
"admin{,/!(src)/**}/!(tsconfig|tsconfig.*).json",
|
package/lib/descriptions.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = {};
|