iobroker.easee 2.0.0 → 3.0.0

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.
@@ -1,34 +1,138 @@
1
1
  "use strict";
2
2
 
3
- const utils = require("@iobroker/adapter-core");
4
-
5
- const axios = require("axios").default;
6
- const signalR = require("@microsoft/signalr");
7
- const objEnum = require("./lib/enum.js");
8
- const AxiosSignalRHttpClient = require("./lib/signalr-http-client.js");
9
- const {
3
+ import { Adapter } from "@iobroker/adapter-core";
4
+ import axios from "axios";
5
+ import * as signalR from "@microsoft/signalr";
6
+ import * as objEnum from "./lib/enum.ts";
7
+ import AxiosSignalRHttpClient from "./lib/signalr-http-client.ts";
8
+ import {
10
9
  CHARGER_STATE_OBSERVATION_IDS,
11
10
  observationsToChargerState,
12
- } = require("./lib/observations.js");
11
+ } from "./lib/observations.ts";
12
+ import type { ChargerObservationState } from "./lib/observations.ts";
13
+
14
+ type Timer = ioBroker.Timeout | undefined;
15
+ type Phase = "p1" | "p2" | "p3";
16
+ type AdapterOptions = ioBroker.AdapterOptions;
17
+
18
+ interface Charger {
19
+ id: string;
20
+ name: string;
21
+ }
22
+
23
+ interface Site {
24
+ id: string;
25
+ currencyId?: string;
26
+ costPerKWh?: number;
27
+ costPerKwhExcludeVat?: number;
28
+ vat?: number;
29
+ circuits?: Array<{ id: string }>;
30
+ }
31
+
32
+ interface ChargerConfig {
33
+ [key: string]: ioBroker.StateValue;
34
+ isEnabled: ioBroker.StateValue;
35
+ phaseMode: ioBroker.StateValue;
36
+ ledStripBrightness: ioBroker.StateValue;
37
+ smartButtonEnabled: ioBroker.StateValue;
38
+ wiFiSSID: ioBroker.StateValue;
39
+ maxChargerCurrent: ioBroker.StateValue;
40
+ circuitMaxCurrentP1: ioBroker.StateValue;
41
+ circuitMaxCurrentP2: ioBroker.StateValue;
42
+ circuitMaxCurrentP3: ioBroker.StateValue;
43
+ }
44
+
45
+ interface ChargerSession {
46
+ year: number;
47
+ month: number;
48
+ totalEnergyUsage: number;
49
+ totalCost: number;
50
+ }
51
+
52
+ interface RetryState {
53
+ delay: number;
54
+ retryAt: number;
55
+ inFlight: Promise<unknown> | null;
56
+ }
57
+
58
+ interface RetryableError extends Error {
59
+ retryDeferred?: boolean;
60
+ }
61
+
62
+ interface DynamicChargerState {
63
+ pendingValues: Partial<Record<Phase, number>>;
64
+ pendingSequences: Partial<Record<Phase, number>>;
65
+ latestSequences: Record<Phase, number>;
66
+ timer: Timer | null;
67
+ retryTimer: Timer | null;
68
+ retryResolve: (() => void) | null;
69
+ queue: Promise<void>;
70
+ }
71
+
72
+ interface DynamicCircuitState {
73
+ values: Record<Phase, number>;
74
+ phaseSequences: Record<Phase, number>;
75
+ timer: Timer | null;
76
+ queue: Promise<void>;
77
+ }
78
+
79
+ interface PendingRetry {
80
+ timer: Timer;
81
+ }
82
+
83
+ interface ChargerValueSet {
84
+ p1: number;
85
+ p2: number;
86
+ p3: number;
87
+ }
88
+
89
+ interface SignalRProductUpdate {
90
+ id: number;
91
+ mid: string;
92
+ dataType: number;
93
+ value: string | number | boolean;
94
+ }
13
95
 
14
96
  //Eigene Variablen
15
97
  const apiUrl = "https://api.easee.com";
16
98
  const apiRequestTimeout = 30000;
17
99
  const sessionRequestInterval = 7 * 60 * 1000;
18
100
  const defaultSessionPollInterval = 60;
19
- const adapterIntervals = {}; //halten von allen Intervallen
101
+ const adapterIntervals: Record<string, Timer | undefined> = {}; //halten von allen Intervallen
20
102
  let accessToken = "";
21
103
  let refreshToken = "";
22
104
  let expireTime = Date.now();
23
105
  let polltime = 30;
24
106
  let logtype = false;
25
107
  const tokenRefreshMarginSeconds = 60;
26
- const arrCharger = [];
27
-
28
- function getRateLimitDelay(error) {
29
- const headers = error?.response?.headers;
30
- const header = (name) =>
31
- headers?.get?.(name) ?? headers?.[name] ?? headers?.[name.toLowerCase()];
108
+ const arrCharger: string[] = [];
109
+
110
+ function getRateLimitDelay(error: unknown): number {
111
+ const errorRecord = isRecord(error) ? error : undefined;
112
+ const responseRecord =
113
+ errorRecord && isRecord(errorRecord.response)
114
+ ? errorRecord.response
115
+ : undefined;
116
+ const responseHeaders =
117
+ axios.isAxiosError(error) && error.response
118
+ ? error.response.headers
119
+ : responseRecord?.headers;
120
+ const headers =
121
+ typeof responseHeaders === "object" && responseHeaders !== null
122
+ ? (responseHeaders as Record<string, unknown>)
123
+ : undefined;
124
+ const header = (name: string): string | undefined => {
125
+ if (!headers) {
126
+ return undefined;
127
+ }
128
+ const value = headers[name] ?? headers[name.toLowerCase()];
129
+ if (typeof value === "string") {
130
+ return value;
131
+ }
132
+ return Array.isArray(value) && typeof value[0] === "string"
133
+ ? value[0]
134
+ : undefined;
135
+ };
32
136
  const retryAfter = header("retry-after");
33
137
  if (retryAfter !== undefined) {
34
138
  const seconds = Number(retryAfter);
@@ -56,48 +160,61 @@ function getRateLimitDelay(error) {
56
160
  }
57
161
  }
58
162
 
59
- return error?.response?.status === 429 ? sessionRequestInterval : 0;
163
+ return (responseRecord?.status ??
164
+ (axios.isAxiosError(error) ? error.response?.status : undefined)) === 429
165
+ ? sessionRequestInterval
166
+ : 0;
60
167
  }
61
168
 
62
- function errorMessage(error) {
169
+ function errorMessage(error: unknown): string {
63
170
  return error instanceof Error ? error.message : String(error);
64
171
  }
65
172
 
66
- class Easee extends utils.Adapter {
67
- constructor(options) {
173
+ function isRecord(value: unknown): value is Record<string, unknown> {
174
+ return typeof value === "object" && value !== null;
175
+ }
176
+
177
+ function isRetryableError(error: unknown): error is RetryableError {
178
+ return (
179
+ error instanceof Error && (error as RetryableError).retryDeferred === true
180
+ );
181
+ }
182
+
183
+ class Easee extends Adapter {
184
+ signalRConnection: signalR.HubConnection | null = null;
185
+ signalRSubscribedChargers = new Set<string>();
186
+ signalRSubscriptionPromises = new Map<string, Promise<void>>();
187
+ unloading = false;
188
+ apiRetryState = new Map<string, RetryState>();
189
+ pendingSiteConfigRetries = new Map<string, PendingRetry>();
190
+ dynamicCircuitCurrentByCharger = new Map<string, DynamicChargerState>();
191
+ dynamicCircuitCurrentByCircuit = new Map<string, DynamicCircuitState>();
192
+ dynamicCircuitCurrentSequence = 0;
193
+ circuitMaxCurrentByCircuit = new Map<string, number>();
194
+ circuitMaxCurrentSequence = 0;
195
+ sessionChargers: string[] = [];
196
+ sessionCursor = 0;
197
+ sessionPollTimer: Timer | null = null;
198
+ sessionInFlight: Promise<void> | null = null;
199
+ sessionAbortController: AbortController | null = null;
200
+ sessionLastRequestAt: number | null = null;
201
+ sessionRetryAt = 0;
202
+ sessionPollMode: "auto" | "manual" = "auto";
203
+ sessionManualInterval = defaultSessionPollInterval;
204
+
205
+ constructor(options: AdapterOptions = { name: "easee" }) {
68
206
  super({
69
207
  ...options,
70
208
  name: "easee",
71
- });
209
+ } as never);
72
210
  this.on("ready", this.onReady.bind(this));
73
211
  this.on("stateChange", this.onStateChange.bind(this));
74
212
  this.on("unload", this.onUnload.bind(this));
75
- this.signalRConnection = null;
76
- this.signalRSubscribedChargers = new Set();
77
- this.signalRSubscriptionPromises = new Map();
78
- this.signalRSubscriptionPromises = new Map();
79
- this.unloading = false;
80
- this.apiRetryState = new Map();
81
- this.pendingSiteConfigRetries = new Map();
82
- this.dynamicCircuitCurrentByCharger = new Map();
83
- this.dynamicCircuitCurrentByCircuit = new Map();
84
- this.dynamicCircuitCurrentSequence = 0;
85
- this.circuitMaxCurrentByCircuit = new Map();
86
- this.circuitMaxCurrentSequence = 0;
87
- this.sessionChargers = [];
88
- this.sessionCursor = 0;
89
- this.sessionPollTimer = null;
90
- this.sessionInFlight = null;
91
- this.sessionAbortController = null;
92
- this.sessionLastRequestAt = null;
93
- this.sessionRetryAt = 0;
94
- this.sessionPollMode = "auto";
95
- this.sessionManualInterval = defaultSessionPollInterval;
96
213
  }
97
214
  /**
98
215
  * SignalR
99
216
  */
100
- async startSignal() {
217
+ async startSignal(): Promise<void> {
101
218
  if (this.unloading || this.signalRConnection !== null) {
102
219
  return;
103
220
  }
@@ -112,7 +229,7 @@ class Easee extends utils.Adapter {
112
229
  .build();
113
230
  this.signalRConnection = connection;
114
231
 
115
- connection.on("ProductUpdate", (data) => {
232
+ connection.on("ProductUpdate", (data: SignalRProductUpdate) => {
116
233
  //haben einen neuen Wert über SignalR erhalten
117
234
  const data_name = objEnum.getNameByEnum(data.id);
118
235
  if (data_name == undefined) {
@@ -129,10 +246,10 @@ class Easee extends utils.Adapter {
129
246
  data.value = data.value == "1";
130
247
  break;
131
248
  case 3:
132
- data.value = parseFloat(data.value);
249
+ data.value = parseFloat(String(data.value));
133
250
  break;
134
251
  case 4:
135
- data.value = parseInt(data.value);
252
+ data.value = parseInt(String(data.value));
136
253
  break;
137
254
  //case 6: JSON
138
255
  }
@@ -174,7 +291,7 @@ class Easee extends utils.Adapter {
174
291
  }
175
292
  }
176
293
 
177
- async subscribeChargerToSignalR(chargerId) {
294
+ async subscribeChargerToSignalR(chargerId: string): Promise<void> {
178
295
  const connection = this.signalRConnection;
179
296
  if (
180
297
  this.unloading ||
@@ -212,12 +329,12 @@ class Easee extends utils.Adapter {
212
329
  return subscription;
213
330
  }
214
331
 
215
- scheduleSignalRReconnect() {
332
+ scheduleSignalRReconnect(): void {
216
333
  if (this.unloading) {
217
334
  return;
218
335
  }
219
- clearTimeout(adapterIntervals.signalRReconnect);
220
- adapterIntervals.signalRReconnect = setTimeout(() => {
336
+ this.clearTimeout(adapterIntervals.signalRReconnect);
337
+ adapterIntervals.signalRReconnect = this.setTimeout(() => {
221
338
  this.startSignal();
222
339
  }, 30000);
223
340
  }
@@ -225,7 +342,7 @@ class Easee extends utils.Adapter {
225
342
  /**
226
343
  * Starten den Adapter
227
344
  */
228
- async onReady() {
345
+ async onReady(): Promise<void> {
229
346
  //initial Status melden
230
347
  await this.setStateAsync("info.connection", false, true);
231
348
 
@@ -245,7 +362,10 @@ class Easee extends utils.Adapter {
245
362
  const lastSessionRequest = await this.getStateAsync(
246
363
  "info.sessionPollLastRequest",
247
364
  );
248
- if (Number.isFinite(lastSessionRequest?.val)) {
365
+ if (
366
+ typeof lastSessionRequest?.val === "number" &&
367
+ Number.isFinite(lastSessionRequest.val)
368
+ ) {
249
369
  this.sessionLastRequestAt = lastSessionRequest.val;
250
370
  }
251
371
 
@@ -294,7 +414,7 @@ class Easee extends utils.Adapter {
294
414
  }
295
415
  }
296
416
 
297
- configureSessionPolling() {
417
+ configureSessionPolling(): void {
298
418
  this.sessionPollMode =
299
419
  this.config.sessionPollMode === "manual" ? "manual" : "auto";
300
420
  const configuredInterval = Number(this.config.sessionPollInterval);
@@ -312,26 +432,34 @@ class Easee extends utils.Adapter {
312
432
  }
313
433
 
314
434
  // Clear all Timeouts and inform users
315
- onUnload(callback) {
435
+ onUnload(callback: () => void): void {
316
436
  this.unloading = true;
317
- clearTimeout(adapterIntervals.readAllStates);
318
- clearTimeout(this.sessionPollTimer);
437
+ this.clearTimeout(adapterIntervals.readAllStates);
438
+ if (this.sessionPollTimer) {
439
+ this.clearTimeout(this.sessionPollTimer);
440
+ }
319
441
  this.sessionPollTimer = null;
320
442
  this.sessionAbortController?.abort();
321
443
  const sessionShutdown = this.sessionInFlight;
322
444
  for (const chargerState of this.dynamicCircuitCurrentByCharger.values()) {
323
- clearTimeout(chargerState.timer);
324
- clearTimeout(chargerState.retryTimer);
445
+ if (chargerState.timer) {
446
+ this.clearTimeout(chargerState.timer);
447
+ }
448
+ if (chargerState.retryTimer) {
449
+ this.clearTimeout(chargerState.retryTimer);
450
+ }
325
451
  chargerState.retryResolve?.();
326
452
  }
327
453
  this.dynamicCircuitCurrentByCharger.clear();
328
454
  for (const circuitState of this.dynamicCircuitCurrentByCircuit.values()) {
329
- clearTimeout(circuitState.timer);
455
+ if (circuitState.timer) {
456
+ this.clearTimeout(circuitState.timer);
457
+ }
330
458
  }
331
459
  this.dynamicCircuitCurrentByCircuit.clear();
332
- clearTimeout(adapterIntervals.signalRReconnect);
460
+ this.clearTimeout(adapterIntervals.signalRReconnect);
333
461
  for (const retry of this.pendingSiteConfigRetries.values()) {
334
- clearTimeout(retry.timer);
462
+ this.clearTimeout(retry.timer);
335
463
  }
336
464
  this.pendingSiteConfigRetries.clear();
337
465
  const signalRShutdown = Promise.resolve(
@@ -355,17 +483,20 @@ class Easee extends utils.Adapter {
355
483
  });
356
484
  }
357
485
 
358
- async requestWithBackoff(key, requestFn) {
486
+ async requestWithBackoff<T>(
487
+ key: string,
488
+ requestFn: () => Promise<T>,
489
+ ): Promise<T> {
359
490
  let retryState = this.apiRetryState.get(key);
360
491
  if (!retryState) {
361
492
  retryState = { delay: 30000, retryAt: 0, inFlight: null };
362
493
  this.apiRetryState.set(key, retryState);
363
494
  }
364
495
  if (retryState.inFlight) {
365
- return retryState.inFlight;
496
+ return (await retryState.inFlight) as T;
366
497
  }
367
498
  if (retryState.retryAt > Date.now()) {
368
- const error = new Error(`Retry deferred for ${key}`);
499
+ const error: RetryableError = new Error(`Retry deferred for ${key}`);
369
500
  error.retryDeferred = true;
370
501
  throw error;
371
502
  }
@@ -391,24 +522,24 @@ class Easee extends utils.Adapter {
391
522
  this.log.warn(
392
523
  `Easee API request failed for ${key}; retrying after ${Math.ceil(delay / 1000)} seconds: ${errorMessage(error)}`,
393
524
  );
394
- const retryError = new Error(errorMessage(error));
525
+ const retryError: RetryableError = new Error(errorMessage(error));
395
526
  retryError.retryDeferred = true;
396
527
  throw retryError;
397
528
  } finally {
398
529
  retryState.inFlight = null;
399
530
  }
400
531
  })();
401
- return retryState.inFlight;
532
+ return (await retryState.inFlight) as T;
402
533
  }
403
534
 
404
535
  async updateSiteConfigWithRetry(
405
- chargerId,
406
- stateId,
407
- description,
408
- update,
409
- sequence,
536
+ chargerId: string,
537
+ stateId: string,
538
+ description: string,
539
+ update: (site: Site) => Promise<void>,
540
+ sequence: number | undefined,
410
541
  requireCircuit = true,
411
- ) {
542
+ ): Promise<void> {
412
543
  if (this.unloading) {
413
544
  return;
414
545
  }
@@ -420,20 +551,22 @@ class Easee extends utils.Adapter {
420
551
  return;
421
552
  }
422
553
  if (requireCircuit) {
423
- const circuitKey = `${site.id}:${site.circuits[0].id}`;
554
+ const circuitKey = `${site.id}:${site.circuits![0].id}`;
424
555
  const latestSequence =
425
556
  this.circuitMaxCurrentByCircuit.get(circuitKey) || 0;
426
- if (sequence < latestSequence) {
557
+ if (sequence !== undefined && sequence < latestSequence) {
427
558
  this.log.info(
428
559
  `Discarded superseded ${description} for ${circuitKey}`,
429
560
  );
430
561
  return;
431
562
  }
432
- this.circuitMaxCurrentByCircuit.set(circuitKey, sequence);
563
+ if (sequence !== undefined) {
564
+ this.circuitMaxCurrentByCircuit.set(circuitKey, sequence);
565
+ }
433
566
  }
434
567
  await update(site);
435
568
  } catch (error) {
436
- if (!error?.retryDeferred || this.unloading) {
569
+ if (!isRetryableError(error) || this.unloading) {
437
570
  this.log.error(`${description} error: ${errorMessage(error)}`);
438
571
  return;
439
572
  }
@@ -441,7 +574,7 @@ class Easee extends utils.Adapter {
441
574
  const retryKey = `${chargerId}:${stateId}`;
442
575
  const existingRetry = this.pendingSiteConfigRetries.get(retryKey);
443
576
  if (existingRetry) {
444
- clearTimeout(existingRetry.timer);
577
+ this.clearTimeout(existingRetry.timer);
445
578
  }
446
579
 
447
580
  const siteRetry = this.apiRetryState.get(`charger site ${chargerId}`);
@@ -449,7 +582,7 @@ class Easee extends utils.Adapter {
449
582
  0,
450
583
  (siteRetry?.retryAt || Date.now()) - Date.now(),
451
584
  );
452
- const timer = setTimeout(() => {
585
+ const timer = this.setTimeout(() => {
453
586
  this.pendingSiteConfigRetries.delete(retryKey);
454
587
  this.updateSiteConfigWithRetry(
455
588
  chargerId,
@@ -468,14 +601,14 @@ class Easee extends utils.Adapter {
468
601
  }
469
602
 
470
603
  async updateDynamicCircuitWithRetry(
471
- chargerId,
472
- chargerState,
473
- changedValues,
474
- phaseSequences,
475
- ) {
604
+ chargerId: string,
605
+ chargerState: DynamicChargerState,
606
+ changedValues: Partial<Record<Phase, number>>,
607
+ phaseSequences: Partial<Record<Phase, number>>,
608
+ ): Promise<void> {
476
609
  while (!this.unloading) {
477
610
  if (
478
- Object.entries(phaseSequences).some(
611
+ (Object.entries(phaseSequences) as [Phase, number][]).some(
479
612
  ([phase, sequence]) =>
480
613
  chargerState.latestSequences[phase] !== sequence,
481
614
  )
@@ -495,7 +628,7 @@ class Easee extends utils.Adapter {
495
628
  }
496
629
 
497
630
  if (
498
- Object.entries(phaseSequences).some(
631
+ (Object.entries(phaseSequences) as [Phase, number][]).some(
499
632
  ([phase, sequence]) =>
500
633
  chargerState.latestSequences[phase] !== sequence,
501
634
  )
@@ -519,8 +652,14 @@ class Easee extends utils.Adapter {
519
652
  this.dynamicCircuitCurrentByCircuit.set(circuitKey, circuitState);
520
653
  }
521
654
  let updated = false;
522
- for (const [phase, value] of Object.entries(changedValues)) {
655
+ for (const [phase, value] of Object.entries(changedValues) as [
656
+ Phase,
657
+ number,
658
+ ][]) {
523
659
  const sequence = phaseSequences[phase];
660
+ if (sequence === undefined) {
661
+ continue;
662
+ }
524
663
  if (sequence >= circuitState.phaseSequences[phase]) {
525
664
  circuitState.values[phase] = value;
526
665
  circuitState.phaseSequences[phase] = sequence;
@@ -534,7 +673,7 @@ class Easee extends utils.Adapter {
534
673
  return;
535
674
  }
536
675
  if (circuitState.timer === null) {
537
- circuitState.timer = setTimeout(() => {
676
+ circuitState.timer = this.setTimeout(() => {
538
677
  circuitState.timer = null;
539
678
  const currentValues = { ...circuitState.values };
540
679
  circuitState.values = { p1: 0, p2: 0, p3: 0 };
@@ -558,7 +697,7 @@ class Easee extends utils.Adapter {
558
697
  }
559
698
  return;
560
699
  } catch (error) {
561
- if (!error?.retryDeferred || this.unloading) {
700
+ if (!isRetryableError(error) || this.unloading) {
562
701
  this.log.error(
563
702
  `Dynamic circuit current update error: ${errorMessage(error)}`,
564
703
  );
@@ -570,9 +709,9 @@ class Easee extends utils.Adapter {
570
709
  0,
571
710
  (siteRetry?.retryAt || Date.now()) - Date.now(),
572
711
  );
573
- await new Promise((resolve) => {
712
+ await new Promise<void>((resolve) => {
574
713
  chargerState.retryResolve = resolve;
575
- chargerState.retryTimer = setTimeout(resolve, delay);
714
+ chargerState.retryTimer = this.setTimeout(resolve, delay);
576
715
  });
577
716
  chargerState.retryResolve = null;
578
717
  chargerState.retryTimer = null;
@@ -581,7 +720,7 @@ class Easee extends utils.Adapter {
581
720
  }
582
721
 
583
722
  /*****************************************************************************************/
584
- async readAllStates() {
723
+ async readAllStates(): Promise<void> {
585
724
  if (this.unloading) {
586
725
  return;
587
726
  }
@@ -648,7 +787,7 @@ class Easee extends utils.Adapter {
648
787
  //Setzen die Config zum Charger
649
788
  await this.setConfigStatus(charger, tmpChargerConfig, tmpChargerSite);
650
789
  } catch (error) {
651
- if (error?.retryDeferred) {
790
+ if (isRetryableError(error)) {
652
791
  continue;
653
792
  }
654
793
  if (typeof error === "string") {
@@ -673,19 +812,17 @@ class Easee extends utils.Adapter {
673
812
  true,
674
813
  );
675
814
  if (!this.unloading) {
676
- adapterIntervals.readAllStates = setTimeout(
815
+ adapterIntervals.readAllStates = this.setTimeout(
677
816
  this.readAllStates.bind(this),
678
817
  polltime * 1000,
679
818
  );
680
819
  }
681
820
  }
682
821
 
683
- updateSessionChargers(chargers) {
822
+ updateSessionChargers(chargers: Charger[]): void {
684
823
  const nextCharger = this.sessionChargers[this.sessionCursor];
685
824
  const previousChargers = this.sessionChargers;
686
- this.sessionChargers = [
687
- ...new Set((chargers || []).map((charger) => charger.id)),
688
- ];
825
+ this.sessionChargers = [...new Set(chargers.map((charger) => charger.id))];
689
826
  const changed =
690
827
  previousChargers.length !== this.sessionChargers.length ||
691
828
  previousChargers.some(
@@ -702,7 +839,9 @@ class Easee extends utils.Adapter {
702
839
  });
703
840
 
704
841
  if (!this.sessionChargers.length) {
705
- clearTimeout(this.sessionPollTimer);
842
+ if (this.sessionPollTimer) {
843
+ this.clearTimeout(this.sessionPollTimer);
844
+ }
706
845
  this.sessionPollTimer = null;
707
846
  return;
708
847
  }
@@ -712,7 +851,7 @@ class Easee extends utils.Adapter {
712
851
  ? 0
713
852
  : Math.max(
714
853
  0,
715
- this.sessionLastRequestAt +
854
+ (this.sessionLastRequestAt ?? 0) +
716
855
  this.getSessionRequestInterval() -
717
856
  Date.now(),
718
857
  );
@@ -720,7 +859,7 @@ class Easee extends utils.Adapter {
720
859
  }
721
860
  }
722
861
 
723
- async updateSessionPollInfo() {
862
+ async updateSessionPollInfo(): Promise<void> {
724
863
  const chargerCount = this.sessionChargers.length;
725
864
  if (!chargerCount) {
726
865
  return;
@@ -745,7 +884,7 @@ class Easee extends utils.Adapter {
745
884
  ]);
746
885
  }
747
886
 
748
- getSessionRequestInterval() {
887
+ getSessionRequestInterval(): number {
749
888
  const chargerCount = this.sessionChargers.length;
750
889
  if (!chargerCount) {
751
890
  return sessionRequestInterval;
@@ -760,7 +899,7 @@ class Easee extends utils.Adapter {
760
899
  );
761
900
  }
762
901
 
763
- scheduleSessionPoll(delay) {
902
+ scheduleSessionPoll(delay: number): void {
764
903
  if (
765
904
  this.unloading ||
766
905
  !this.sessionChargers.length ||
@@ -768,8 +907,10 @@ class Easee extends utils.Adapter {
768
907
  ) {
769
908
  return;
770
909
  }
771
- clearTimeout(this.sessionPollTimer);
772
- this.sessionPollTimer = setTimeout(() => {
910
+ if (this.sessionPollTimer) {
911
+ this.clearTimeout(this.sessionPollTimer);
912
+ }
913
+ this.sessionPollTimer = this.setTimeout(() => {
773
914
  this.sessionPollTimer = null;
774
915
  this.pollNextSession().catch((error) => {
775
916
  this.log.warn(`Session polling failed: ${errorMessage(error)}`);
@@ -777,7 +918,7 @@ class Easee extends utils.Adapter {
777
918
  }, delay);
778
919
  }
779
920
 
780
- async pollNextSession() {
921
+ async pollNextSession(): Promise<void> {
781
922
  if (
782
923
  this.unloading ||
783
924
  this.sessionInFlight ||
@@ -861,7 +1002,7 @@ class Easee extends utils.Adapter {
861
1002
  this.sessionAbortController = null;
862
1003
  const delay = Math.max(
863
1004
  0,
864
- this.sessionLastRequestAt +
1005
+ (this.sessionLastRequestAt ?? 0) +
865
1006
  this.getSessionRequestInterval() -
866
1007
  Date.now(),
867
1008
  );
@@ -873,7 +1014,7 @@ class Easee extends utils.Adapter {
873
1014
  }
874
1015
 
875
1016
  //Is called if a subscribed state changes
876
- onStateChange(id, state) {
1017
+ onStateChange(id: string, state: ioBroker.State | null | undefined): void {
877
1018
  if (state) {
878
1019
  // The state was changed
879
1020
  this.log.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
@@ -898,7 +1039,7 @@ class Easee extends utils.Adapter {
898
1039
 
899
1040
  await this.changeMaxCircuitConfig(
900
1041
  site.id,
901
- site.circuits[0].id,
1042
+ site.circuits![0].id,
902
1043
  state.val,
903
1044
  );
904
1045
  this.log.debug("Changes sent to API");
@@ -925,7 +1066,7 @@ class Easee extends utils.Adapter {
925
1066
  };
926
1067
  this.dynamicCircuitCurrentByCharger.set(chargerId, currentState);
927
1068
  }
928
- let phase;
1069
+ let phase: Phase | undefined;
929
1070
  switch (tmpControl[4]) {
930
1071
  case "dynamicCircuitCurrentP1":
931
1072
  phase = "p1";
@@ -937,13 +1078,18 @@ class Easee extends utils.Adapter {
937
1078
  phase = "p3";
938
1079
  break;
939
1080
  }
1081
+ if (!phase) {
1082
+ return;
1083
+ }
940
1084
  currentState.pendingValues[phase] = Number(state.val);
941
1085
  currentState.pendingSequences[phase] = ++this
942
1086
  .dynamicCircuitCurrentSequence;
943
1087
  currentState.latestSequences[phase] =
944
- currentState.pendingSequences[phase];
945
- clearTimeout(currentState.timer);
946
- currentState.timer = setTimeout(() => {
1088
+ currentState.pendingSequences[phase] ?? 0;
1089
+ if (currentState.timer) {
1090
+ this.clearTimeout(currentState.timer);
1091
+ }
1092
+ currentState.timer = this.setTimeout(() => {
947
1093
  currentState.timer = null;
948
1094
  const changedValues = { ...currentState.pendingValues };
949
1095
  const phaseSequences = { ...currentState.pendingSequences };
@@ -1053,7 +1199,10 @@ class Easee extends utils.Adapter {
1053
1199
  ***********************************************************************/
1054
1200
 
1055
1201
  //Setzen alle Status für Charger
1056
- async setNewStatusToCharger(charger, charger_states) {
1202
+ async setNewStatusToCharger(
1203
+ charger: Charger,
1204
+ charger_states: ChargerObservationState,
1205
+ ): Promise<void> {
1057
1206
  await this.setStateAsync(`${charger.id}.name`, charger.name, true);
1058
1207
  await this.setStateAsync(
1059
1208
  `${charger.id}.status.cableLocked`,
@@ -1218,7 +1367,11 @@ class Easee extends utils.Adapter {
1218
1367
  }
1219
1368
 
1220
1369
  //Setzen alle Status für Config
1221
- async setConfigStatus(charger, charger_config, charger_site) {
1370
+ async setConfigStatus(
1371
+ charger: Charger,
1372
+ charger_config: ChargerConfig,
1373
+ charger_site: Site | null | undefined,
1374
+ ): Promise<void> {
1222
1375
  await this.setStateAsync(`${charger.id}.config.isEnabled`, {
1223
1376
  val: charger_config.isEnabled,
1224
1377
  ack: true,
@@ -1301,7 +1454,7 @@ class Easee extends utils.Adapter {
1301
1454
  **************************************************************************/
1302
1455
 
1303
1456
  //Get Token from API
1304
- async login(username, password) {
1457
+ async login(username: string, password: string): Promise<boolean> {
1305
1458
  try {
1306
1459
  const response = await axios.post(
1307
1460
  `${apiUrl}/api/accounts/login`,
@@ -1339,7 +1492,7 @@ class Easee extends utils.Adapter {
1339
1492
  }
1340
1493
 
1341
1494
  //GET net Token from API
1342
- async refreshToken() {
1495
+ async refreshToken(): Promise<void> {
1343
1496
  return await axios
1344
1497
  .post(
1345
1498
  `${apiUrl}/api/accounts/refresh_token`,
@@ -1373,9 +1526,9 @@ class Easee extends utils.Adapter {
1373
1526
  }
1374
1527
 
1375
1528
  //Lese alle Charger aus
1376
- async getAllCharger() {
1529
+ async getAllCharger(): Promise<Charger[] | undefined> {
1377
1530
  return await axios
1378
- .get(`${apiUrl}/api/chargers`, {
1531
+ .get<Charger[]>(`${apiUrl}/api/chargers`, {
1379
1532
  headers: { Authorization: `Bearer ${accessToken}` },
1380
1533
  timeout: apiRequestTimeout,
1381
1534
  })
@@ -1386,11 +1539,12 @@ class Easee extends utils.Adapter {
1386
1539
  })
1387
1540
  .catch((error) => {
1388
1541
  this.log.error(error);
1542
+ return undefined;
1389
1543
  });
1390
1544
  }
1391
1545
 
1392
1546
  // Lese den Charger aus
1393
- async getChargerState(charger_id) {
1547
+ async getChargerState(charger_id: string): Promise<ChargerObservationState> {
1394
1548
  const response = await this.requestWithBackoff(
1395
1549
  `charger observations ${charger_id}`,
1396
1550
  () =>
@@ -1405,7 +1559,7 @@ class Easee extends utils.Adapter {
1405
1559
  return observationsToChargerState(response.data);
1406
1560
  }
1407
1561
 
1408
- async getChargerConfig(charger_id) {
1562
+ async getChargerConfig(charger_id: string): Promise<ChargerConfig> {
1409
1563
  const response = await this.requestWithBackoff(
1410
1564
  `charger config ${charger_id}`,
1411
1565
  () =>
@@ -1419,7 +1573,7 @@ class Easee extends utils.Adapter {
1419
1573
  return response.data;
1420
1574
  }
1421
1575
 
1422
- async getChargerSite(charger_id) {
1576
+ async getChargerSite(charger_id: string): Promise<Site> {
1423
1577
  const response = await this.requestWithBackoff(
1424
1578
  `charger site ${charger_id}`,
1425
1579
  () =>
@@ -1433,7 +1587,10 @@ class Easee extends utils.Adapter {
1433
1587
  return response.data;
1434
1588
  }
1435
1589
 
1436
- async getChargerSession(charger_id, signal) {
1590
+ async getChargerSession(
1591
+ charger_id: string,
1592
+ signal?: AbortSignal,
1593
+ ): Promise<ChargerSession[]> {
1437
1594
  const response = await this.requestWithBackoff(
1438
1595
  `charger session ${charger_id}`,
1439
1596
  () =>
@@ -1448,7 +1605,12 @@ class Easee extends utils.Adapter {
1448
1605
  return response.data;
1449
1606
  }
1450
1607
 
1451
- async changeChargingPrice(charger_id, site, netPrice, vat) {
1608
+ async changeChargingPrice(
1609
+ charger_id: string,
1610
+ site: Site,
1611
+ netPrice: number,
1612
+ vat: number,
1613
+ ): Promise<void> {
1452
1614
  if (!site || site.id === undefined) {
1453
1615
  throw new Error(`No site data available for charger ${charger_id}`);
1454
1616
  }
@@ -1494,7 +1656,7 @@ class Easee extends utils.Adapter {
1494
1656
  this.log.info("Charging price update successful");
1495
1657
  }
1496
1658
 
1497
- async startCharging(id) {
1659
+ async startCharging(id: string): Promise<void> {
1498
1660
  return await axios
1499
1661
  .post(
1500
1662
  `${apiUrl}/api/chargers/${id}/commands/start_charging`,
@@ -1511,7 +1673,7 @@ class Easee extends utils.Adapter {
1511
1673
  });
1512
1674
  }
1513
1675
 
1514
- async stopCharging(id) {
1676
+ async stopCharging(id: string): Promise<void> {
1515
1677
  return await axios
1516
1678
  .post(
1517
1679
  `${apiUrl}/api/chargers/${id}/commands/stop_charging`,
@@ -1528,7 +1690,7 @@ class Easee extends utils.Adapter {
1528
1690
  });
1529
1691
  }
1530
1692
 
1531
- async pauseCharging(id) {
1693
+ async pauseCharging(id: string): Promise<void> {
1532
1694
  return await axios
1533
1695
  .post(
1534
1696
  `${apiUrl}/api/chargers/${id}/commands/pause_charging`,
@@ -1545,7 +1707,7 @@ class Easee extends utils.Adapter {
1545
1707
  });
1546
1708
  }
1547
1709
 
1548
- async resumeCharging(id) {
1710
+ async resumeCharging(id: string): Promise<void> {
1549
1711
  return await axios
1550
1712
  .post(
1551
1713
  `${apiUrl}/api/chargers/${id}/commands/resume_charging`,
@@ -1562,7 +1724,7 @@ class Easee extends utils.Adapter {
1562
1724
  });
1563
1725
  }
1564
1726
 
1565
- async rebootCharging(id) {
1727
+ async rebootCharging(id: string): Promise<void> {
1566
1728
  return await axios
1567
1729
  .post(
1568
1730
  `${apiUrl}/api/chargers/${id}/commands/reboot`,
@@ -1579,7 +1741,11 @@ class Easee extends utils.Adapter {
1579
1741
  });
1580
1742
  }
1581
1743
 
1582
- async changeConfig(id, configvalue, value) {
1744
+ async changeConfig(
1745
+ id: string,
1746
+ configvalue: string,
1747
+ value: ioBroker.StateValue,
1748
+ ): Promise<void> {
1583
1749
  this.log.debug(
1584
1750
  JSON.stringify({
1585
1751
  [configvalue]: value,
@@ -1604,7 +1770,11 @@ class Easee extends utils.Adapter {
1604
1770
  }
1605
1771
 
1606
1772
  //circuitMaxCurrentPX
1607
- async changeMaxCircuitConfig(site_id, circuit_id, value) {
1773
+ async changeMaxCircuitConfig(
1774
+ site_id: string,
1775
+ circuit_id: string,
1776
+ value: ioBroker.StateValue,
1777
+ ): Promise<void> {
1608
1778
  return await axios
1609
1779
  .post(
1610
1780
  `${apiUrl}/api/sites/${site_id}/circuits/${circuit_id}/settings`,
@@ -1626,7 +1796,11 @@ class Easee extends utils.Adapter {
1626
1796
  }
1627
1797
 
1628
1798
  //dynamicCircuitCurrentPX
1629
- async changeCircuitConfig(site_id, circuit_id, currentValues) {
1799
+ async changeCircuitConfig(
1800
+ site_id: string,
1801
+ circuit_id: string,
1802
+ currentValues: ChargerValueSet,
1803
+ ): Promise<void> {
1630
1804
  //Der Wert darf nur für 3 Fach Werte aktualisiert werden
1631
1805
  await axios
1632
1806
  .post(
@@ -1656,7 +1830,7 @@ class Easee extends utils.Adapter {
1656
1830
  * Funktionen zum erstellen der Objekte der Reading
1657
1831
  ***********************************************************************/
1658
1832
 
1659
- async setAllStatusObjects(charger) {
1833
+ async setAllStatusObjects(charger: Charger): Promise<void> {
1660
1834
  //Legen die Steuerungsbutton für jeden Charger an
1661
1835
  await this.setObjectNotExistsAsync(`${charger.id}.control.start`, {
1662
1836
  type: "state",
@@ -2145,7 +2319,10 @@ class Easee extends utils.Adapter {
2145
2319
  }
2146
2320
 
2147
2321
  /*************** Session Reading ****************/
2148
- async setNewSessionToCharger(charger, charger_session) {
2322
+ async setNewSessionToCharger(
2323
+ charger: Pick<Charger, "id">,
2324
+ charger_session: ChargerSession[],
2325
+ ): Promise<void> {
2149
2326
  this.log.debug(JSON.stringify(charger_session));
2150
2327
  for (const session of charger_session) {
2151
2328
  //für jeden Monat errechnen wir das?
@@ -2235,7 +2412,7 @@ class Easee extends utils.Adapter {
2235
2412
  }
2236
2413
 
2237
2414
  /*************** Config Reading ****************/
2238
- async setAllConfigObjects(charger) {
2415
+ async setAllConfigObjects(charger: Charger): Promise<void> {
2239
2416
  await this.setObjectNotExistsAsync(`${charger.id}.config.costPerKWh`, {
2240
2417
  type: "state",
2241
2418
  common: {
@@ -2508,11 +2685,13 @@ class Easee extends utils.Adapter {
2508
2685
  }
2509
2686
  }
2510
2687
 
2511
- if (module.parent) {
2512
- // Export the constructor in compact mode
2513
- module.exports = (options) => new Easee(options);
2514
- module.exports.Easee = Easee;
2515
- } else {
2516
- // otherwise start the instance directly
2688
+ const createAdapter = Object.assign(
2689
+ (options: AdapterOptions = { name: "easee" }) => new Easee(options),
2690
+ { Easee },
2691
+ );
2692
+
2693
+ if (require.main === module) {
2517
2694
  new Easee();
2518
2695
  }
2696
+
2697
+ export = createAdapter;