iobroker.renault 0.0.13 → 0.0.14

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.
Files changed (3) hide show
  1. package/io-package.json +114 -119
  2. package/main.js +568 -513
  3. package/package.json +73 -73
package/main.js CHANGED
@@ -13,549 +13,604 @@ const Json2iob = require("./lib/json2iob");
13
13
  const descriptions = require("./lib/descriptions");
14
14
 
15
15
  class Renault extends utils.Adapter {
16
- /**
17
- * @param {Partial<utils.AdapterOptions>} [options={}]
18
- */
19
- constructor(options) {
20
- super({
21
- ...options,
22
- name: "renault",
23
- });
24
- this.on("ready", this.onReady.bind(this));
25
- this.on("stateChange", this.onStateChange.bind(this));
26
- this.on("unload", this.onUnload.bind(this));
27
- this.deviceArray = [];
28
- this.json2iob = new Json2iob(this);
29
- this.ignoreState = [];
30
- this.firstUpdate = true;
31
- }
16
+ /**
17
+ * @param {Partial<utils.AdapterOptions>} [options={}]
18
+ */
19
+ constructor(options) {
20
+ super({
21
+ ...options,
22
+ name: "renault",
23
+ });
24
+ this.on("ready", this.onReady.bind(this));
25
+ this.on("stateChange", this.onStateChange.bind(this));
26
+ this.on("unload", this.onUnload.bind(this));
27
+ this.deviceArray = [];
28
+ this.json2iob = new Json2iob(this);
29
+ this.ignoreState = {};
30
+ this.firstUpdate = true;
31
+ }
32
32
 
33
- /**
34
- * Is called when databases are connected and adapter received configuration.
35
- */
36
- async onReady() {
37
- // Reset the connection indicator during startup
38
- this.setState("info.connection", false, true);
39
- if (this.config.interval < 0.5) {
40
- this.log.info("Set interval to minimum 0.5");
41
- this.config.interval = 0.5;
42
- }
43
- this.requestClient = axios.create();
44
- this.updateInterval = null;
45
- this.reLoginTimeout = null;
46
- this.refreshTokenTimeout = null;
47
- this.country = this.config.country || "de";
48
- this.session = {};
49
- //DE API Key
50
- this.apiKey = "3_7PLksOyBRkHv126x5WhHb-5pqC1qFR8pQjxSeLB6nhAnPERTUlwnYoznHSxwX668";
51
- this.apiKeyUpdate = "VAX7XYKGfa92yMvXculCkEFyfZbuM7Ss";
52
- if (this.config.apiKeyUpdate) {
53
- this.apiKeyUpdate = this.config.apiKeyUpdate;
54
- }
33
+ /**
34
+ * Is called when databases are connected and adapter received configuration.
35
+ */
36
+ async onReady() {
37
+ // Reset the connection indicator during startup
38
+ this.setState("info.connection", false, true);
39
+ if (this.config.interval < 0.5) {
40
+ this.log.info("Set interval to minimum 0.5");
41
+ this.config.interval = 0.5;
42
+ }
43
+ this.requestClient = axios.create();
44
+ this.updateInterval = null;
45
+ this.reLoginTimeout = null;
46
+ this.refreshTokenTimeout = null;
47
+ this.country = this.config.country || "de";
48
+ this.session = {};
49
+ //DE API Key
50
+ this.apiKey = "3_7PLksOyBRkHv126x5WhHb-5pqC1qFR8pQjxSeLB6nhAnPERTUlwnYoznHSxwX668";
51
+ this.apiKeyUpdate = "VAX7XYKGfa92yMvXculCkEFyfZbuM7Ss";
52
+ if (this.config.apiKeyUpdate) {
53
+ this.apiKeyUpdate = this.config.apiKeyUpdate;
54
+ }
55
55
 
56
- this.subscribeStates("*");
56
+ this.subscribeStates("*");
57
57
 
58
- await this.login();
58
+ await this.login();
59
59
 
60
- if (this.session.id_token && this.session_data && this.account) {
61
- await this.getDeviceList();
62
- await this.updateDevices();
63
- this.updateInterval = setInterval(async () => {
64
- await this.updateDevices();
65
- }, this.config.interval * 60 * 1000);
66
- this.refreshTokenInterval = setInterval(() => {
67
- this.refreshToken();
68
- }, 3500 * 1000);
69
- }
60
+ if (this.session.id_token && this.session_data && this.account) {
61
+ await this.getDeviceList();
62
+ await this.updateDevices();
63
+ this.updateInterval = setInterval(async () => {
64
+ await this.updateDevices();
65
+ }, this.config.interval * 60 * 1000);
66
+ this.refreshTokenInterval = setInterval(() => {
67
+ this.refreshToken();
68
+ }, 3500 * 1000);
70
69
  }
71
- async login() {
72
- this.session_data = await this.requestClient({
73
- method: "post",
74
- url: "https://accounts.eu1.gigya.com/accounts.login",
75
- headers: {
76
- "User-Agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
77
- Accept: "*/*",
78
- "Accept-Language": "de-de",
79
- "Cache-Control": "no-cache",
80
- "Content-Type": "application/x-www-form-urlencoded",
81
- },
82
- data: qs.stringify({
83
- apikey: this.apiKey,
84
- format: "json",
85
- httpStatusCodes: "false",
86
- loginID: this.config.username,
87
- password: this.config.password,
88
- sdk: "js_latest",
89
- include: "profile,data",
90
- }),
91
- })
92
- .then((res) => {
93
- this.log.debug(JSON.stringify(res.data));
94
- return res.data.sessionInfo;
95
- })
96
- .catch((error) => {
97
- this.log.error(error);
98
- if (error.response) {
99
- this.log.error(JSON.stringify(error.response.data));
100
- }
101
- });
102
- if (!this.session_data) {
103
- this.log.error("No session found for this account");
104
- return;
70
+ }
71
+ async login() {
72
+ this.session_data = await this.requestClient({
73
+ method: "post",
74
+ url: "https://accounts.eu1.gigya.com/accounts.login",
75
+ headers: {
76
+ "User-Agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
77
+ Accept: "*/*",
78
+ "Accept-Language": "de-de",
79
+ "Cache-Control": "no-cache",
80
+ "Content-Type": "application/x-www-form-urlencoded",
81
+ },
82
+ data: qs.stringify({
83
+ apikey: this.apiKey,
84
+ format: "json",
85
+ httpStatusCodes: "false",
86
+ loginID: this.config.username,
87
+ password: this.config.password,
88
+ sdk: "js_latest",
89
+ include: "profile,data",
90
+ }),
91
+ })
92
+ .then((res) => {
93
+ this.log.debug(JSON.stringify(res.data));
94
+ return res.data.sessionInfo;
95
+ })
96
+ .catch((error) => {
97
+ this.log.error(error);
98
+ if (error.response) {
99
+ this.log.error(JSON.stringify(error.response.data));
105
100
  }
106
- await this.requestClient({
107
- method: "post",
108
- url: "https://accounts.eu1.gigya.com/accounts.getJWT",
109
- headers: {
110
- "User-Agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
111
- Accept: "*/*",
112
- "Accept-Language": "de-de",
113
- "Cache-Control": "no-cache",
114
- "Content-Type": "application/x-www-form-urlencoded",
115
- },
116
- data: qs.stringify({
117
- format: "json",
118
- login_token: this.session_data.cookieValue,
119
- sdk: "js_latest",
120
- fields: "data.personId,data.gigyaDataCenter",
121
- apikey: this.apiKey,
122
- expiration: "3600",
123
- }),
124
- })
125
- .then((res) => {
126
- this.log.debug(JSON.stringify(res.data));
127
- this.session = res.data;
128
- this.setState("info.connection", true, true);
129
- })
130
- .catch((error) => {
131
- this.log.error(error);
132
- if (error.response) {
133
- this.log.error(JSON.stringify(error.response.data));
134
- }
135
- });
136
- await this.requestClient({
137
- method: "post",
138
- url: "https://apis.renault.com/myr/api/v1/connection?&country=DE&product=MYRENAULT&locale=de-DE&displayAccounts=MYRENAULT",
139
- headers: {
140
- "Content-Type": "application/json",
141
- Accept: "*/*",
142
- "User-Agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
143
- apiKey: this.apiKeyUpdate,
144
- "Accept-Language": "de-de",
145
- "x-gigya-id_token": this.session.id_token,
146
- },
147
- })
148
- .then((res) => {
149
- this.log.debug(JSON.stringify(res.data));
150
-
151
- const filteredAccounts = res.data.currentUser.accounts.filter(function (el) {
152
- return (el.accountType === "MYRENAULT" || el.accountType === "MYDACIA") && el.accountStatus === "ACTIVE";
153
- });
154
- if (filteredAccounts.length === 0) {
155
- this.log.error("No Account found");
156
- this.log.error("All accounts: " + res.data.currentUser.accounts);
157
- this.log.error("Filtered accounts: " + filteredAccounts);
158
- return;
159
- }
160
- this.account = filteredAccounts[0];
161
- })
162
- .catch((error) => {
163
- this.log.error("Error while getting account");
164
- this.log.error(error);
165
- if (error.response) {
166
- this.log.error(JSON.stringify(error.response.data));
167
- if (error.response.data.indexOf("apikey") !== -1) {
168
- this.log.error("Wrong API Key. Please update API Key in adapter settings");
169
- }
170
- }
171
- });
101
+ });
102
+ if (!this.session_data) {
103
+ this.log.error("No session found for this account");
104
+ return;
172
105
  }
173
- async getDeviceList() {
174
- await this.requestClient({
175
- method: "get",
176
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/vehicles?country=" + this.country + "&oms=false",
177
- headers: {
178
- apikey: this.apiKeyUpdate,
179
- "content-type": "application/json",
180
- accept: "*/*",
181
- "user-agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
182
- "accept-language": "de-de",
183
- "x-gigya-id_token": this.session.id_token,
184
- },
185
- })
186
- .then(async (res) => {
187
- this.log.debug(JSON.stringify(res.data));
188
-
189
- for (const device of res.data.vehicleLinks) {
190
- this.deviceArray.push(device.vin);
191
- let name = device.vehicleDetails.modelSCR;
192
- if (device.vehicleDetails.model && device.vehicleDetails.model.label) {
193
- name += device.vehicleDetails.model.label;
194
- }
195
- await this.setObjectNotExistsAsync(device.vin, {
196
- type: "device",
197
- common: {
198
- name: name,
199
- },
200
- native: {},
201
- });
202
- await this.setObjectNotExistsAsync(device.vin + ".remote", {
203
- type: "channel",
204
- common: {
205
- name: "Remote Controls",
206
- },
207
- native: {},
208
- });
209
- await this.setObjectNotExistsAsync(device.vin + ".general", {
210
- type: "channel",
211
- common: {
212
- name: "General Information",
213
- },
214
- native: {},
215
- });
106
+ await this.requestClient({
107
+ method: "post",
108
+ url: "https://accounts.eu1.gigya.com/accounts.getJWT",
109
+ headers: {
110
+ "User-Agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
111
+ Accept: "*/*",
112
+ "Accept-Language": "de-de",
113
+ "Cache-Control": "no-cache",
114
+ "Content-Type": "application/x-www-form-urlencoded",
115
+ },
116
+ data: qs.stringify({
117
+ format: "json",
118
+ login_token: this.session_data.cookieValue,
119
+ sdk: "js_latest",
120
+ fields: "data.personId,data.gigyaDataCenter",
121
+ apikey: this.apiKey,
122
+ expiration: "3600",
123
+ }),
124
+ })
125
+ .then((res) => {
126
+ this.log.debug(JSON.stringify(res.data));
127
+ this.session = res.data;
128
+ this.setState("info.connection", true, true);
129
+ })
130
+ .catch((error) => {
131
+ this.log.error(error);
132
+ if (error.response) {
133
+ this.log.error(JSON.stringify(error.response.data));
134
+ }
135
+ });
136
+ await this.requestClient({
137
+ method: "post",
138
+ url: "https://apis.renault.com/myr/api/v1/connection?&country=DE&product=MYRENAULT&locale=de-DE&displayAccounts=MYRENAULT",
139
+ headers: {
140
+ "Content-Type": "application/json",
141
+ Accept: "*/*",
142
+ "User-Agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
143
+ apiKey: this.apiKeyUpdate,
144
+ "Accept-Language": "de-de",
145
+ "x-gigya-id_token": this.session.id_token,
146
+ },
147
+ })
148
+ .then((res) => {
149
+ this.log.debug(JSON.stringify(res.data));
216
150
 
217
- const remoteArray = [
218
- { command: "actions/hvac-start", name: "True = Start, False = Stop" },
219
- { command: "hvac-temperature", name: "HVAC Temperature", type: "number", role: "value" },
220
- { command: "actions/charging-start", name: "True = Start, False = Stop" },
221
- { command: "charge/pause-resume", name: "True = Start, False = Stop" },
222
- { command: "charge/start", name: "True = Start, False = Stop" },
223
- { command: "refresh", name: "True = Refresh Data" },
224
- ];
225
- remoteArray.forEach((remote) => {
226
- this.setObjectNotExists(device.vin + ".remote." + remote.command, {
227
- type: "state",
228
- common: {
229
- name: remote.name || "",
230
- type: remote.type || "boolean",
231
- role: remote.role || "boolean",
232
- write: true,
233
- read: true,
234
- },
235
- native: {},
236
- });
237
- });
238
- this.json2iob.parse(device.vin + ".general", device);
239
- }
240
- })
241
- .catch((error) => {
242
- this.log.error("Error while getting vehicle list");
243
- this.log.error(error);
244
- error.response && this.log.error(JSON.stringify(error.response.data));
245
- });
246
- }
151
+ const filteredAccounts = res.data.currentUser.accounts.filter(function (el) {
152
+ return (el.accountType === "MYRENAULT" || el.accountType === "MYDACIA") && el.accountStatus === "ACTIVE";
153
+ });
154
+ if (filteredAccounts.length === 0) {
155
+ this.log.error("No Account found");
156
+ this.log.error("All accounts: " + res.data.currentUser.accounts);
157
+ this.log.error("Filtered accounts: " + filteredAccounts);
158
+ return;
159
+ }
160
+ this.account = filteredAccounts[0];
161
+ })
162
+ .catch((error) => {
163
+ this.log.error("Error while getting account");
164
+ this.log.error(error);
165
+ if (error.response) {
166
+ this.log.error(JSON.stringify(error.response.data));
167
+ if (error.response.data && JSON.stringify(error.response.data).indexOf("apikey") !== -1) {
168
+ this.log.error("Wrong API Key. Please update API Key in adapter settings");
169
+ }
170
+ }
171
+ });
172
+ }
173
+ async getDeviceList() {
174
+ await this.requestClient({
175
+ method: "get",
176
+ url:
177
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
178
+ this.account.accountId +
179
+ "/vehicles?country=" +
180
+ this.country +
181
+ "&oms=false",
182
+ headers: {
183
+ apikey: this.apiKeyUpdate,
184
+ "content-type": "application/json",
185
+ accept: "*/*",
186
+ "user-agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
187
+ "accept-language": "de-de",
188
+ "x-gigya-id_token": this.session.id_token,
189
+ },
190
+ })
191
+ .then(async (res) => {
192
+ this.log.debug(JSON.stringify(res.data));
247
193
 
248
- async updateDevices() {
249
- const curDate = new Date().toISOString().split("T")[0];
194
+ for (const device of res.data.vehicleLinks) {
195
+ this.deviceArray.push(device.vin);
196
+ let name = device.vehicleDetails.modelSCR;
197
+ if (device.vehicleDetails.model && device.vehicleDetails.model.label) {
198
+ name += device.vehicleDetails.model.label;
199
+ }
250
200
 
251
- const statusArray = [
252
- {
253
- path: "battery-status",
254
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v2/cars/$vin/battery-status?country=" + this.country,
255
- desc: "Battery status of the car",
256
- },
257
- {
258
- path: "battery-inhibition-status",
259
- url:
260
- "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
261
- this.account.accountId +
262
- "/kamereon/kca/car-adapter/v1/cars/$vin/battery-inhibition-status?country=" +
263
- this.country,
264
- desc: "Battery inhibition status of the car",
265
- },
266
- {
267
- path: "cockpit",
268
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v1/cars/$vin/cockpit?country=" + this.country,
269
- desc: "Status of the car",
270
- },
271
- {
272
- path: "cockpitv2",
273
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v2/cars/$vin/cockpit?country=" + this.country,
274
- desc: "Statusv2 of the car",
275
- },
276
- {
277
- path: "charge-mode",
278
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v1/cars/$vin/charge-mode?country=" + this.country,
279
- desc: "Charge mode of the car",
280
- },
281
- {
282
- path: "hvac-status",
283
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v1/cars/$vin/hvac-status?country=" + this.country,
284
- desc: "HVAC status of the car",
285
- },
286
- {
287
- path: "hvac-settings",
288
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v1/cars/$vin/hvac-settings?country=" + this.country,
289
- desc: "HVAC settings of the car",
290
- },
291
- {
292
- path: "charging-settings",
293
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v1/cars/$vin/charging-settings?country=" + this.country,
294
- desc: "Charging settings of the car",
295
- },
296
- {
297
- path: "charge-history",
298
- url:
299
- "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
300
- this.account.accountId +
301
- "/kamereon/kca/car-adapter/v1/cars/$vin/charge-history?type=day&start=1970-01-01&end=" +
302
- curDate +
303
- "&country=" +
304
- this.country,
305
- desc: "Charging history of the car",
306
- },
307
- {
308
- path: "charges",
309
- url:
310
- "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
311
- this.account.accountId +
312
- "/kamereon/kca/car-adapter/v1/cars/$vin/charges?start=1970-01-01&end=" +
313
- curDate +
314
- "&country=" +
315
- this.country,
316
- desc: "Charges of the car",
201
+ this.ignoreState[device.vin] = [];
202
+ await this.setObjectNotExistsAsync(device.vin, {
203
+ type: "device",
204
+ common: {
205
+ name: name,
317
206
  },
318
- {
319
- path: "lock-status",
320
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v1/cars/$vin/lock-status?country=" + this.country,
321
- desc: "Lock status of the car",
207
+ native: {},
208
+ });
209
+ await this.setObjectNotExistsAsync(device.vin + ".remote", {
210
+ type: "channel",
211
+ common: {
212
+ name: "Remote Controls",
322
213
  },
323
- {
324
- path: "res-state",
325
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v1/cars/$vin/res-state?country=" + this.country,
326
- desc: "Res status of the car",
214
+ native: {},
215
+ });
216
+ await this.setObjectNotExistsAsync(device.vin + ".general", {
217
+ type: "channel",
218
+ common: {
219
+ name: "WIRD NICHT AKTUALISIERT",
327
220
  },
328
- {
329
- path: "location",
330
- url: "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" + this.account.accountId + "/kamereon/kca/car-adapter/v1/cars/$vin/location?country=" + this.country,
331
- desc: "Location of the car",
332
- },
333
- ];
221
+ native: {},
222
+ });
334
223
 
335
- const headers = {
336
- apikey: this.apiKeyUpdate,
337
- "content-type": "application/json",
338
- accept: "*/*",
339
- "user-agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
340
- "accept-language": "de-de",
341
- "x-gigya-id_token": this.session.id_token,
342
- };
343
- for (const vin of this.deviceArray) {
344
- for (const element of statusArray) {
345
- if (this.ignoreState.includes(element.path)) {
346
- return;
347
- }
348
- const url = element.url.replace("$vin", vin);
349
-
350
- await this.requestClient({
351
- method: "get",
352
- url: url,
353
- headers: headers,
354
- })
355
- .then((res) => {
356
- this.log.debug(JSON.stringify(res.data));
357
- if (!res.data) {
358
- return;
359
- }
360
- let data = res.data;
361
- if (res.data.data && res.data.data.attributes) {
362
- data = res.data.data.attributes;
363
- }
224
+ const remoteArray = [
225
+ { command: "actions/hvac-start", name: "True = Start, False = Stop" },
226
+ { command: "hvac-temperature", name: "HVAC Temperature", type: "number", role: "value" },
227
+ { command: "actions/charging-start", name: "True = Start, False = Stop" },
228
+ { command: "charge/pause-resume", name: "True = Start, False = Stop" },
229
+ { command: "charge/start", name: "True = Start, False = Stop" },
230
+ { command: "refresh", name: "True = Refresh Data" },
231
+ ];
232
+ remoteArray.forEach((remote) => {
233
+ this.setObjectNotExists(device.vin + ".remote." + remote.command, {
234
+ type: "state",
235
+ common: {
236
+ name: remote.name || "",
237
+ type: remote.type || "boolean",
238
+ role: remote.role || "boolean",
239
+ write: true,
240
+ read: true,
241
+ },
242
+ native: {},
243
+ });
244
+ });
245
+ this.json2iob.parse(device.vin + ".general", device);
246
+ }
247
+ })
248
+ .catch((error) => {
249
+ this.log.error("Error while getting vehicle list");
250
+ this.log.error(error);
251
+ error.response && this.log.error(JSON.stringify(error.response.data));
252
+ });
253
+ }
364
254
 
365
- const forceIndex = null;
366
- const preferedArrayName = null;
255
+ async updateDevices() {
256
+ const curDate = new Date().toISOString().split("T")[0];
367
257
 
368
- this.json2iob.parse(vin + "." + element.path, data, { forceIndex: forceIndex, preferedArrayName: preferedArrayName, channelName: element.desc });
369
- })
370
- .catch((error) => {
371
- if (error.response) {
372
- if (error.response.status === 401) {
373
- error.response && this.log.debug(JSON.stringify(error.response.data));
374
- this.log.info(element.path + " receive 401 error. Refresh Token in 60 seconds");
375
- clearTimeout(this.refreshTokenTimeout);
376
- this.refreshTokenTimeout = setTimeout(() => {
377
- this.refreshToken();
378
- }, 1000 * 60);
258
+ const statusArray = [
259
+ {
260
+ path: "battery-status",
261
+ url:
262
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
263
+ this.account.accountId +
264
+ "/kamereon/kca/car-adapter/v2/cars/$vin/battery-status?country=" +
265
+ this.country,
266
+ desc: "Battery status of the car",
267
+ },
268
+ {
269
+ path: "battery-inhibition-status",
270
+ url:
271
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
272
+ this.account.accountId +
273
+ "/kamereon/kca/car-adapter/v1/cars/$vin/battery-inhibition-status?country=" +
274
+ this.country,
275
+ desc: "Battery inhibition status of the car",
276
+ },
277
+ {
278
+ path: "cockpit",
279
+ url:
280
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
281
+ this.account.accountId +
282
+ "/kamereon/kca/car-adapter/v1/cars/$vin/cockpit?country=" +
283
+ this.country,
284
+ desc: "Status of the car",
285
+ },
286
+ {
287
+ path: "cockpitv2",
288
+ url:
289
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
290
+ this.account.accountId +
291
+ "/kamereon/kca/car-adapter/v2/cars/$vin/cockpit?country=" +
292
+ this.country,
293
+ desc: "Statusv2 of the car",
294
+ },
295
+ {
296
+ path: "charge-mode",
297
+ url:
298
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
299
+ this.account.accountId +
300
+ "/kamereon/kca/car-adapter/v1/cars/$vin/charge-mode?country=" +
301
+ this.country,
302
+ desc: "Charge mode of the car",
303
+ },
304
+ {
305
+ path: "hvac-status",
306
+ url:
307
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
308
+ this.account.accountId +
309
+ "/kamereon/kca/car-adapter/v1/cars/$vin/hvac-status?country=" +
310
+ this.country,
311
+ desc: "HVAC status of the car",
312
+ },
313
+ {
314
+ path: "hvac-settings",
315
+ url:
316
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
317
+ this.account.accountId +
318
+ "/kamereon/kca/car-adapter/v1/cars/$vin/hvac-settings?country=" +
319
+ this.country,
320
+ desc: "HVAC settings of the car",
321
+ },
322
+ {
323
+ path: "charging-settings",
324
+ url:
325
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
326
+ this.account.accountId +
327
+ "/kamereon/kca/car-adapter/v1/cars/$vin/charging-settings?country=" +
328
+ this.country,
329
+ desc: "Charging settings of the car",
330
+ },
331
+ {
332
+ path: "charge-history",
333
+ url:
334
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
335
+ this.account.accountId +
336
+ "/kamereon/kca/car-adapter/v1/cars/$vin/charge-history?type=day&start=1970-01-01&end=" +
337
+ curDate +
338
+ "&country=" +
339
+ this.country,
340
+ desc: "Charging history of the car",
341
+ },
342
+ {
343
+ path: "charges",
344
+ url:
345
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
346
+ this.account.accountId +
347
+ "/kamereon/kca/car-adapter/v1/cars/$vin/charges?start=1970-01-01&end=" +
348
+ curDate +
349
+ "&country=" +
350
+ this.country,
351
+ desc: "Charges of the car",
352
+ },
353
+ {
354
+ path: "lock-status",
355
+ url:
356
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
357
+ this.account.accountId +
358
+ "/kamereon/kca/car-adapter/v1/cars/$vin/lock-status?country=" +
359
+ this.country,
360
+ desc: "Lock status of the car",
361
+ },
362
+ {
363
+ path: "res-state",
364
+ url:
365
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
366
+ this.account.accountId +
367
+ "/kamereon/kca/car-adapter/v1/cars/$vin/res-state?country=" +
368
+ this.country,
369
+ desc: "Res status of the car",
370
+ },
371
+ {
372
+ path: "location",
373
+ url:
374
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
375
+ this.account.accountId +
376
+ "/kamereon/kca/car-adapter/v1/cars/$vin/location?country=" +
377
+ this.country,
378
+ desc: "Location of the car",
379
+ },
380
+ ];
379
381
 
380
- return;
381
- }
382
- if (this.firstUpdate) {
383
- if (
384
- error.response.status === 403 ||
385
- error.response.status === 404 ||
386
- error.response.status === 500 ||
387
- error.response.status === 501 ||
388
- error.response.status === 502 ||
389
- error.response.status === 400
390
- ) {
391
- this.ignoreState.push(element.path);
392
- this.log.info("Feature not found. Ignore " + element.path + " for updates.");
393
- this.log.info(error);
394
- error.response && this.log.info(JSON.stringify(error.response.data));
395
- return;
396
- }
397
- }
398
- }
399
- this.log.error(url);
400
- this.log.error(error);
401
- error.response && this.log.error(JSON.stringify(error.response.data));
402
- });
403
- }
404
- }
405
- this.firstUpdate = false;
406
- }
407
- async refreshToken() {
408
- if (!this.session_data) {
409
- this.log.error("No session found relogin");
410
- await this.login();
411
- return;
382
+ const headers = {
383
+ apikey: this.apiKeyUpdate,
384
+ "content-type": "application/json",
385
+ accept: "*/*",
386
+ "user-agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
387
+ "accept-language": "de-de",
388
+ "x-gigya-id_token": this.session.id_token,
389
+ };
390
+ for (const vin of this.deviceArray) {
391
+ for (const element of statusArray) {
392
+ if (this.ignoreState[vin] && this.ignoreState[vin].includes(element.path)) {
393
+ continue;
412
394
  }
395
+ const url = element.url.replace("$vin", vin);
396
+
413
397
  await this.requestClient({
414
- method: "post",
415
- url: "https://accounts.eu1.gigya.com/accounts.getJWT",
416
- headers: {
417
- "User-Agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
418
- Accept: "*/*",
419
- "Accept-Language": "de-de",
420
- "Cache-Control": "no-cache",
421
- "Content-Type": "application/x-www-form-urlencoded",
422
- },
423
- data: qs.stringify({
424
- format: "json",
425
- login_token: this.session_data.cookieValue,
426
- sdk: "js_latest",
427
- fields: "data.personId,data.gigyaDataCenter",
428
- apikey: this.apiKey,
429
- expiration: "3600",
430
- }),
398
+ method: "get",
399
+ url: url,
400
+ headers: headers,
431
401
  })
432
- .then((res) => {
433
- this.log.debug(JSON.stringify(res.data));
434
- this.session = res.data;
435
- this.setState("info.connection", true, true);
436
- })
437
- .catch((error) => {
438
- this.log.error("refresh token failed");
439
- this.log.error(error);
440
- error.response && this.log.error(JSON.stringify(error.response.data));
441
- this.log.error("Start relogin in 1min");
442
- this.reLoginTimeout = setTimeout(() => {
443
- this.login();
444
- }, 1000 * 60 * 1);
402
+ .then((res) => {
403
+ this.log.debug(JSON.stringify(res.data));
404
+ if (!res.data) {
405
+ return;
406
+ }
407
+ let data = res.data;
408
+ if (res.data.data && res.data.data.attributes) {
409
+ data = res.data.data.attributes;
410
+ }
411
+
412
+ const forceIndex = null;
413
+ const preferedArrayName = null;
414
+
415
+ this.json2iob.parse(vin + "." + element.path, data, {
416
+ forceIndex: forceIndex,
417
+ preferedArrayName: preferedArrayName,
418
+ channelName: element.desc,
445
419
  });
420
+ })
421
+ .catch((error) => {
422
+ if (error.response) {
423
+ if (error.response.status === 401) {
424
+ error.response && this.log.debug(JSON.stringify(error.response.data));
425
+ this.log.info(element.path + " receive 401 error. Refresh Token in 60 seconds");
426
+ clearTimeout(this.refreshTokenTimeout);
427
+ this.refreshTokenTimeout = setTimeout(() => {
428
+ this.refreshToken();
429
+ }, 1000 * 60);
430
+
431
+ return;
432
+ }
433
+ if (this.firstUpdate) {
434
+ if (
435
+ error.response.status === 403 ||
436
+ error.response.status === 404 ||
437
+ error.response.status === 500 ||
438
+ error.response.status === 501 ||
439
+ error.response.status === 502 ||
440
+ error.response.status === 400
441
+ ) {
442
+ this.ignoreState[vin].push(element.path);
443
+ this.log.info("Feature not found for " + vin + ". Ignore " + element.path + " for updates.");
444
+ this.log.debug(error);
445
+ error.response && this.log.debug(JSON.stringify(error.response.data));
446
+ return;
447
+ }
448
+ }
449
+ }
450
+ this.log.error(url);
451
+ this.log.error(error);
452
+ error.response && this.log.error(JSON.stringify(error.response.data));
453
+ });
454
+ }
446
455
  }
447
- sleep(ms) {
448
- return new Promise((resolve) => setTimeout(resolve, ms));
456
+ this.firstUpdate = false;
457
+ }
458
+ async refreshToken() {
459
+ if (!this.session_data) {
460
+ this.log.error("No session found relogin");
461
+ await this.login();
462
+ return;
449
463
  }
450
- toCamelCase(string) {
451
- if (!string) {
452
- return;
453
- }
454
- const camelC = string.replace(/-([a-z])/g, function (g) {
455
- return g[1].toUpperCase();
456
- });
457
- return camelC.charAt(0).toUpperCase() + camelC.slice(1);
464
+ await this.requestClient({
465
+ method: "post",
466
+ url: "https://accounts.eu1.gigya.com/accounts.getJWT",
467
+ headers: {
468
+ "User-Agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
469
+ Accept: "*/*",
470
+ "Accept-Language": "de-de",
471
+ "Cache-Control": "no-cache",
472
+ "Content-Type": "application/x-www-form-urlencoded",
473
+ },
474
+ data: qs.stringify({
475
+ format: "json",
476
+ login_token: this.session_data.cookieValue,
477
+ sdk: "js_latest",
478
+ fields: "data.personId,data.gigyaDataCenter",
479
+ apikey: this.apiKey,
480
+ expiration: "3600",
481
+ }),
482
+ })
483
+ .then((res) => {
484
+ this.log.debug(JSON.stringify(res.data));
485
+ this.session = res.data;
486
+ this.setState("info.connection", true, true);
487
+ })
488
+ .catch((error) => {
489
+ this.log.error("refresh token failed");
490
+ this.log.error(error);
491
+ error.response && this.log.error(JSON.stringify(error.response.data));
492
+ this.log.error("Start relogin in 1min");
493
+ this.reLoginTimeout = setTimeout(() => {
494
+ this.login();
495
+ }, 1000 * 60 * 1);
496
+ });
497
+ }
498
+ sleep(ms) {
499
+ return new Promise((resolve) => setTimeout(resolve, ms));
500
+ }
501
+ toCamelCase(string) {
502
+ if (!string) {
503
+ return;
458
504
  }
459
- /**
460
- * Is called when adapter shuts down - callback has to be called under any circumstances!
461
- * @param {() => void} callback
462
- */
463
- onUnload(callback) {
464
- try {
465
- this.setState("info.connection", false, true);
466
- clearTimeout(this.refreshTimeout);
467
- clearTimeout(this.reLoginTimeout);
468
- clearTimeout(this.refreshTokenTimeout);
469
- clearInterval(this.updateInterval);
470
- clearInterval(this.refreshTokenInterval);
471
- callback();
472
- } catch (e) {
473
- callback();
474
- }
505
+ const camelC = string.replace(/-([a-z])/g, function (g) {
506
+ return g[1].toUpperCase();
507
+ });
508
+ return camelC.charAt(0).toUpperCase() + camelC.slice(1);
509
+ }
510
+ /**
511
+ * Is called when adapter shuts down - callback has to be called under any circumstances!
512
+ * @param {() => void} callback
513
+ */
514
+ onUnload(callback) {
515
+ try {
516
+ this.setState("info.connection", false, true);
517
+ clearTimeout(this.refreshTimeout);
518
+ clearTimeout(this.reLoginTimeout);
519
+ clearTimeout(this.refreshTokenTimeout);
520
+ clearInterval(this.updateInterval);
521
+ clearInterval(this.refreshTokenInterval);
522
+ callback();
523
+ } catch (e) {
524
+ callback();
475
525
  }
526
+ }
476
527
 
477
- /**
478
- * Is called if a subscribed state changes
479
- * @param {string} id
480
- * @param {ioBroker.State | null | undefined} state
481
- */
482
- async onStateChange(id, state) {
483
- if (state) {
484
- if (!state.ack) {
485
- const deviceId = id.split(".")[2];
486
- const path = id.split(".")[4];
487
- if (path === "hvac-temperature") {
488
- return;
489
- }
490
- if (path === "refresh") {
491
- this.log.info("Force refresh");
492
- this.updateDevices();
493
- return;
494
- }
495
- if (!this.account) {
496
- this.log.error("No account found");
497
- return;
498
- }
499
- const command = path.split("/")[1];
500
- const data = { data: { type: this.toCamelCase(command), attributes: { action: state.val ? "start" : "cancel" } } };
501
- if (command === "hvac-start") {
502
- const temperatureState = await this.getStateAsync(deviceId + ".remote.hvac-temperature");
503
- if (temperatureState) {
504
- data.data.attributes.targetTemperature = temperatureState.val ? temperatureState.val : 21;
505
- } else {
506
- data.data.attributes.targetTemperature = 21;
507
- }
508
- }
509
- const url =
510
- "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
511
- this.account.accountId +
512
- "/kamereon/kca/car-adapter/v1/cars/" +
513
- deviceId +
514
- "/" +
515
- path +
516
- "?country=" +
517
- this.country;
518
- this.log.debug(JSON.stringify(data));
519
- this.log.debug(url);
520
- await this.requestClient({
521
- method: "post",
522
- url: url,
523
- headers: {
524
- apikey: this.apiKeyUpdate,
525
- "content-type": "application/vnd.api+json",
526
- accept: "*/*",
527
- "user-agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
528
- "accept-language": "de-de",
529
- "x-gigya-id_token": this.session.id_token,
530
- },
531
- data: data,
532
- })
533
- .then((res) => {
534
- this.log.debug(JSON.stringify(res.data));
535
- return res.data;
536
- })
537
- .catch((error) => {
538
- this.log.error(error);
539
- if (error.response) {
540
- this.log.error(JSON.stringify(error.response.data));
541
- }
542
- });
543
- clearTimeout(this.refreshTimeout);
544
- this.refreshTimeout = setTimeout(async () => {
545
- await this.updateDevices();
546
- }, 20 * 1000);
547
- }
528
+ /**
529
+ * Is called if a subscribed state changes
530
+ * @param {string} id
531
+ * @param {ioBroker.State | null | undefined} state
532
+ */
533
+ async onStateChange(id, state) {
534
+ if (state) {
535
+ if (!state.ack) {
536
+ const deviceId = id.split(".")[2];
537
+ const path = id.split(".")[4];
538
+ if (path === "hvac-temperature") {
539
+ return;
540
+ }
541
+ if (path === "refresh") {
542
+ this.log.info("Force refresh");
543
+ this.updateDevices();
544
+ return;
545
+ }
546
+ if (!this.account) {
547
+ this.log.error("No account found");
548
+ return;
548
549
  }
550
+ const command = path.split("/")[1];
551
+ let action = state.val ? "start" : "cancel";
552
+ if (path === "charging-start") {
553
+ action = state.val ? "start" : "stop";
554
+ }
555
+ const data = { data: { type: this.toCamelCase(command), attributes: { action: action } } };
556
+ if (command === "hvac-start") {
557
+ const temperatureState = await this.getStateAsync(deviceId + ".remote.hvac-temperature");
558
+ if (temperatureState) {
559
+ data.data.attributes.targetTemperature = temperatureState.val ? temperatureState.val : 21;
560
+ } else {
561
+ data.data.attributes.targetTemperature = 21;
562
+ }
563
+ }
564
+ const url =
565
+ "https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/accounts/" +
566
+ this.account.accountId +
567
+ "/kamereon/kca/car-adapter/v1/cars/" +
568
+ deviceId +
569
+ "/" +
570
+ path +
571
+ "?country=" +
572
+ this.country;
573
+ this.log.debug(JSON.stringify(data));
574
+ this.log.debug(url);
575
+ await this.requestClient({
576
+ method: "post",
577
+ url: url,
578
+ headers: {
579
+ apikey: this.apiKeyUpdate,
580
+ "content-type": "application/vnd.api+json",
581
+ accept: "*/*",
582
+ "user-agent": "MYRenault/39 CFNetwork/1312 Darwin/21.0.0",
583
+ "accept-language": "de-de",
584
+ "x-gigya-id_token": this.session.id_token,
585
+ },
586
+ data: data,
587
+ })
588
+ .then((res) => {
589
+ this.log.debug(JSON.stringify(res.data));
590
+ return res.data;
591
+ })
592
+ .catch((error) => {
593
+ this.log.error(error);
594
+ if (error.response) {
595
+ this.log.error(JSON.stringify(error.response.data));
596
+ }
597
+ });
598
+ clearTimeout(this.refreshTimeout);
599
+ this.refreshTimeout = setTimeout(async () => {
600
+ await this.updateDevices();
601
+ }, 20 * 1000);
602
+ }
549
603
  }
604
+ }
550
605
  }
551
606
 
552
607
  if (require.main !== module) {
553
- // Export the constructor in compact mode
554
- /**
555
- * @param {Partial<utils.AdapterOptions>} [options={}]
556
- */
557
- module.exports = (options) => new Renault(options);
608
+ // Export the constructor in compact mode
609
+ /**
610
+ * @param {Partial<utils.AdapterOptions>} [options={}]
611
+ */
612
+ module.exports = (options) => new Renault(options);
558
613
  } else {
559
- // otherwise start the instance directly
560
- new Renault();
614
+ // otherwise start the instance directly
615
+ new Renault();
561
616
  }