fix: avoid duplicated slash in connection url
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index aa251e395be27922cea3bb87bedeec384b12701e..080349eae731cbe27e9ba6b00354da0d40a66f7e 100644 (file)
@@ -1,65 +1,18 @@
-// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
+// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
-import { createHash } from 'node:crypto';
-import { EventEmitter } from 'node:events';
-import { type FSWatcher, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
-import { dirname, join } from 'node:path';
-import { URL } from 'node:url';
-import { parentPort } from 'node:worker_threads';
+import { createHash, randomInt } from 'node:crypto'
+import { EventEmitter } from 'node:events'
+import { existsSync, type FSWatcher, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
+import { dirname, join } from 'node:path'
+import { URL } from 'node:url'
+import { parentPort } from 'node:worker_threads'
 
-import { millisecondsToSeconds, secondsToMilliseconds } from 'date-fns';
-import merge from 'just-merge';
-import { type RawData, WebSocket } from 'ws';
+import { millisecondsToSeconds, secondsToMilliseconds } from 'date-fns'
+import { mergeDeepRight, once } from 'rambda'
+import { type RawData, WebSocket } from 'ws'
 
-import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator';
-import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel';
-import {
-  addConfigurationKey,
-  deleteConfigurationKey,
-  getConfigurationKey,
-  setConfigurationKeyValue,
-} from './ConfigurationKeyUtils';
-import {
-  buildConnectorsMap,
-  checkChargingStation,
-  checkConfiguration,
-  checkConnectorsConfiguration,
-  checkStationInfoConnectorStatus,
-  checkTemplate,
-  createBootNotificationRequest,
-  createSerialNumber,
-  getAmperageLimitationUnitDivider,
-  getBootConnectorStatus,
-  getChargingStationConnectorChargingProfilesPowerLimit,
-  getChargingStationId,
-  getDefaultVoltageOut,
-  getHashId,
-  getIdTagsFile,
-  getMaxNumberOfEvses,
-  getNumberOfReservableConnectors,
-  getPhaseRotationValue,
-  hasReservationExpired,
-  initializeConnectorsMapStatus,
-  propagateSerialNumber,
-  stationTemplateToStationInfo,
-  warnTemplateKeysDeprecation,
-} from './Helpers';
-import { IdTagsCache } from './IdTagsCache';
-import {
-  OCPP16IncomingRequestService,
-  OCPP16RequestService,
-  OCPP16ResponseService,
-  OCPP16ServiceUtils,
-  OCPP20IncomingRequestService,
-  OCPP20RequestService,
-  OCPP20ResponseService,
-  type OCPPIncomingRequestService,
-  type OCPPRequestService,
-  OCPPServiceUtils,
-} from './ocpp';
-import { SharedLRUCache } from './SharedLRUCache';
-import { BaseError, OCPPError } from '../exception';
-import { PerformanceStatistics } from '../performance';
+import { BaseError, OCPPError } from '../exception/index.js'
+import { PerformanceStatistics } from '../performance/index.js'
 import {
   type AutomaticTransactionGeneratorConfiguration,
   AvailabilityType,
@@ -70,6 +23,7 @@ import {
   ChargingStationEvents,
   type ChargingStationInfo,
   type ChargingStationOcppConfiguration,
+  type ChargingStationOptions,
   type ChargingStationTemplate,
   type ConnectorStatus,
   ConnectorStatusEnum,
@@ -83,14 +37,11 @@ import {
   FirmwareStatus,
   type FirmwareStatusNotificationRequest,
   type FirmwareStatusNotificationResponse,
-  type FirmwareUpgrade,
   type HeartbeatRequest,
   type HeartbeatResponse,
   type IncomingRequest,
   type IncomingRequestCommand,
-  type JsonType,
   MessageType,
-  type MeterValue,
   MeterValueMeasurand,
   type MeterValuesRequest,
   type MeterValuesResponse,
@@ -105,519 +56,641 @@ import {
   type Response,
   StandardParametersKey,
   type Status,
-  type StatusNotificationRequest,
-  type StatusNotificationResponse,
-  StopTransactionReason,
+  type StopTransactionReason,
   type StopTransactionRequest,
   type StopTransactionResponse,
   SupervisionUrlDistribution,
   SupportedFeatureProfiles,
-  VendorParametersKey,
-  Voltage,
-  type WSError,
+  type Voltage,
   WebSocketCloseEventStatusCode,
-  type WsOptions,
-} from '../types';
+  type WSError,
+  type WsOptions
+} from '../types/index.js'
 import {
   ACElectricUtils,
   AsyncLock,
   AsyncLockType,
-  Configuration,
-  Constants,
-  DCElectricUtils,
+  buildAddedMessage,
   buildChargingStationAutomaticTransactionGeneratorConfiguration,
   buildConnectorsStatus,
+  buildDeletedMessage,
   buildEvsesStatus,
   buildStartedMessage,
   buildStoppedMessage,
   buildUpdatedMessage,
-  cloneObject,
+  clone,
+  Configuration,
+  Constants,
   convertToBoolean,
+  convertToDate,
   convertToInt,
+  DCElectricUtils,
   exponentialDelay,
   formatDurationMilliSeconds,
   formatDurationSeconds,
-  getRandomInteger,
   getWebSocketCloseEventStatusString,
   handleFileException,
   isNotEmptyArray,
   isNotEmptyString,
-  isNullOrUndefined,
-  isUndefined,
-  logPrefix,
   logger,
+  logPrefix,
   min,
-  once,
   roundTo,
   secureRandom,
   sleep,
-  watchJsonFile,
-} from '../utils';
+  watchJsonFile
+} from '../utils/index.js'
+import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator.js'
+import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel.js'
+import {
+  addConfigurationKey,
+  deleteConfigurationKey,
+  getConfigurationKey,
+  setConfigurationKeyValue
+} from './ConfigurationKeyUtils.js'
+import {
+  buildConnectorsMap,
+  buildTemplateName,
+  checkChargingStation,
+  checkConfiguration,
+  checkConnectorsConfiguration,
+  checkStationInfoConnectorStatus,
+  checkTemplate,
+  createBootNotificationRequest,
+  createSerialNumber,
+  getAmperageLimitationUnitDivider,
+  getBootConnectorStatus,
+  getChargingStationConnectorChargingProfilesPowerLimit,
+  getChargingStationId,
+  getDefaultVoltageOut,
+  getHashId,
+  getIdTagsFile,
+  getMaxNumberOfEvses,
+  getNumberOfReservableConnectors,
+  getPhaseRotationValue,
+  hasFeatureProfile,
+  hasReservationExpired,
+  initializeConnectorsMapStatus,
+  propagateSerialNumber,
+  setChargingStationOptions,
+  stationTemplateToStationInfo,
+  warnTemplateKeysDeprecation
+} from './Helpers.js'
+import { IdTagsCache } from './IdTagsCache.js'
+import {
+  buildMeterValue,
+  buildTransactionEndMeterValue,
+  getMessageTypeString,
+  OCPP16IncomingRequestService,
+  OCPP16RequestService,
+  OCPP16ResponseService,
+  OCPP20IncomingRequestService,
+  OCPP20RequestService,
+  OCPP20ResponseService,
+  type OCPPIncomingRequestService,
+  type OCPPRequestService,
+  sendAndSetConnectorStatus
+} from './ocpp/index.js'
+import { SharedLRUCache } from './SharedLRUCache.js'
 
 export class ChargingStation extends EventEmitter {
-  public readonly index: number;
-  public readonly templateFile: string;
-  public stationInfo!: ChargingStationInfo;
-  public started: boolean;
-  public starting: boolean;
-  public idTagsCache: IdTagsCache;
-  public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined;
-  public ocppConfiguration!: ChargingStationOcppConfiguration | undefined;
-  public wsConnection: WebSocket | null;
-  public readonly connectors: Map<number, ConnectorStatus>;
-  public readonly evses: Map<number, EvseStatus>;
-  public readonly requests: Map<string, CachedRequest>;
-  public performanceStatistics!: PerformanceStatistics | undefined;
-  public heartbeatSetInterval?: NodeJS.Timeout;
-  public ocppRequestService!: OCPPRequestService;
-  public bootNotificationRequest!: BootNotificationRequest;
-  public bootNotificationResponse!: BootNotificationResponse | undefined;
-  public powerDivider!: number;
-  private stopping: boolean;
-  private configurationFile!: string;
-  private configurationFileHash!: string;
-  private connectorsConfigurationHash!: string;
-  private evsesConfigurationHash!: string;
-  private automaticTransactionGeneratorConfiguration?: AutomaticTransactionGeneratorConfiguration;
-  private ocppIncomingRequestService!: OCPPIncomingRequestService;
-  private readonly messageBuffer: Set<string>;
-  private configuredSupervisionUrl!: URL;
-  private autoReconnectRetryCount: number;
-  private templateFileWatcher!: FSWatcher | undefined;
-  private templateFileHash!: string;
-  private readonly sharedLRUCache: SharedLRUCache;
-  private webSocketPingSetInterval?: NodeJS.Timeout;
-  private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel;
-  private flushMessageBufferSetInterval?: NodeJS.Timeout;
-
-  constructor(index: number, templateFile: string) {
-    super();
-    this.started = false;
-    this.starting = false;
-    this.stopping = false;
-    this.wsConnection = null;
-    this.autoReconnectRetryCount = 0;
-    this.index = index;
-    this.templateFile = templateFile;
-    this.connectors = new Map<number, ConnectorStatus>();
-    this.evses = new Map<number, EvseStatus>();
-    this.requests = new Map<string, CachedRequest>();
-    this.messageBuffer = new Set<string>();
-    this.sharedLRUCache = SharedLRUCache.getInstance();
-    this.idTagsCache = IdTagsCache.getInstance();
-    this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this);
-
+  public readonly index: number
+  public readonly templateFile: string
+  public stationInfo?: ChargingStationInfo
+  public started: boolean
+  public starting: boolean
+  public idTagsCache: IdTagsCache
+  public automaticTransactionGenerator?: AutomaticTransactionGenerator
+  public ocppConfiguration?: ChargingStationOcppConfiguration
+  public wsConnection: WebSocket | null
+  public readonly connectors: Map<number, ConnectorStatus>
+  public readonly evses: Map<number, EvseStatus>
+  public readonly requests: Map<string, CachedRequest>
+  public performanceStatistics?: PerformanceStatistics
+  public heartbeatSetInterval?: NodeJS.Timeout
+  public ocppRequestService!: OCPPRequestService
+  public bootNotificationRequest?: BootNotificationRequest
+  public bootNotificationResponse?: BootNotificationResponse
+  public powerDivider?: number
+  private stopping: boolean
+  private configurationFile!: string
+  private configurationFileHash!: string
+  private connectorsConfigurationHash!: string
+  private evsesConfigurationHash!: string
+  private automaticTransactionGeneratorConfiguration?: AutomaticTransactionGeneratorConfiguration
+  private ocppIncomingRequestService!: OCPPIncomingRequestService
+  private readonly messageBuffer: Set<string>
+  private configuredSupervisionUrl!: URL
+  private wsConnectionRetried: boolean
+  private wsConnectionRetryCount: number
+  private templateFileWatcher?: FSWatcher
+  private templateFileHash!: string
+  private readonly sharedLRUCache: SharedLRUCache
+  private wsPingSetInterval?: NodeJS.Timeout
+  private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel
+  private flushMessageBufferSetInterval?: NodeJS.Timeout
+
+  constructor (index: number, templateFile: string, options?: ChargingStationOptions) {
+    super()
+    this.started = false
+    this.starting = false
+    this.stopping = false
+    this.wsConnection = null
+    this.wsConnectionRetried = false
+    this.wsConnectionRetryCount = 0
+    this.index = index
+    this.templateFile = templateFile
+    this.connectors = new Map<number, ConnectorStatus>()
+    this.evses = new Map<number, EvseStatus>()
+    this.requests = new Map<string, CachedRequest>()
+    this.messageBuffer = new Set<string>()
+    this.sharedLRUCache = SharedLRUCache.getInstance()
+    this.idTagsCache = IdTagsCache.getInstance()
+    this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this)
+
+    this.on(ChargingStationEvents.added, () => {
+      parentPort?.postMessage(buildAddedMessage(this))
+    })
+    this.on(ChargingStationEvents.deleted, () => {
+      parentPort?.postMessage(buildDeletedMessage(this))
+    })
     this.on(ChargingStationEvents.started, () => {
-      parentPort?.postMessage(buildStartedMessage(this));
-    });
+      parentPort?.postMessage(buildStartedMessage(this))
+    })
     this.on(ChargingStationEvents.stopped, () => {
-      parentPort?.postMessage(buildStoppedMessage(this));
-    });
+      parentPort?.postMessage(buildStoppedMessage(this))
+    })
     this.on(ChargingStationEvents.updated, () => {
-      parentPort?.postMessage(buildUpdatedMessage(this));
-    });
+      parentPort?.postMessage(buildUpdatedMessage(this))
+    })
+    this.on(ChargingStationEvents.accepted, () => {
+      this.startMessageSequence(
+        this.wsConnectionRetried
+          ? true
+          : this.getAutomaticTransactionGeneratorConfiguration()?.stopAbsoluteDuration
+      ).catch((error: unknown) => {
+        logger.error(`${this.logPrefix()} Error while starting the message sequence:`, error)
+      })
+      this.wsConnectionRetried = false
+    })
+    this.on(ChargingStationEvents.rejected, () => {
+      this.wsConnectionRetried = false
+    })
+    this.on(ChargingStationEvents.disconnected, () => {
+      try {
+        this.internalStopMessageSequence()
+      } catch (error) {
+        logger.error(
+          `${this.logPrefix()} Error while stopping the internal message sequence:`,
+          error
+        )
+      }
+    })
+
+    this.initialize(options)
+
+    this.add()
 
-    this.initialize();
+    if (this.stationInfo?.autoStart === true) {
+      this.start()
+    }
   }
 
-  public get hasEvses(): boolean {
-    return this.connectors.size === 0 && this.evses.size > 0;
+  public get hasEvses (): boolean {
+    return this.connectors.size === 0 && this.evses.size > 0
   }
 
-  private get wsConnectionUrl(): URL {
+  public get wsConnectionUrl (): URL {
+    const wsConnectionBaseUrlStr = `${
+      this.stationInfo?.supervisionUrlOcppConfiguration === true &&
+      isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) &&
+      isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey)?.value)
+        ? getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey)?.value
+        : this.configuredSupervisionUrl.href
+    }`
     return new URL(
-      `${
-        this.stationInfo?.supervisionUrlOcppConfiguration === true &&
-        isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
-        isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)?.value)
-          ? getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)!.value
-          : this.configuredSupervisionUrl.href
-      }/${this.stationInfo.chargingStationId}`,
-    );
+      `${wsConnectionBaseUrlStr}${!wsConnectionBaseUrlStr.endsWith('/') ? '/' : ''}${this.stationInfo?.chargingStationId}`
+    )
   }
 
   public logPrefix = (): string => {
-    if (isNotEmptyString(this?.stationInfo?.chargingStationId)) {
-      return logPrefix(` ${this?.stationInfo?.chargingStationId} |`);
+    if (
+      this instanceof ChargingStation &&
+      this.stationInfo != null &&
+      isNotEmptyString(this.stationInfo.chargingStationId)
+    ) {
+      return logPrefix(` ${this.stationInfo.chargingStationId} |`)
     }
-    let stationTemplate: ChargingStationTemplate | undefined;
+    let stationTemplate: ChargingStationTemplate | undefined
     try {
       stationTemplate = JSON.parse(
-        readFileSync(this.templateFile, 'utf8'),
-      ) as ChargingStationTemplate;
+        readFileSync(this.templateFile, 'utf8')
+      ) as ChargingStationTemplate
     } catch {
-      stationTemplate = undefined;
+      // Ignore
     }
-    return logPrefix(` ${getChargingStationId(this.index, stationTemplate)} |`);
-  };
+    return logPrefix(` ${getChargingStationId(this.index, stationTemplate)} |`)
+  }
 
-  public hasIdTags(): boolean {
-    return isNotEmptyArray(this.idTagsCache.getIdTags(getIdTagsFile(this.stationInfo)!));
+  public hasIdTags (): boolean {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    return isNotEmptyArray(this.idTagsCache.getIdTags(getIdTagsFile(this.stationInfo!)!))
   }
 
-  public getNumberOfPhases(stationInfo?: ChargingStationInfo): number {
-    const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo;
+  public getNumberOfPhases (stationInfo?: ChargingStationInfo): number {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const localStationInfo = stationInfo ?? this.stationInfo!
     switch (this.getCurrentOutType(stationInfo)) {
       case CurrentType.AC:
-        return localStationInfo.numberOfPhases ?? 3;
+        return localStationInfo.numberOfPhases ?? 3
       case CurrentType.DC:
-        return 0;
+        return 0
     }
   }
 
-  public isWebSocketConnectionOpened(): boolean {
-    return this?.wsConnection?.readyState === WebSocket.OPEN;
+  public isWebSocketConnectionOpened (): boolean {
+    return this.wsConnection?.readyState === WebSocket.OPEN
   }
 
-  public inUnknownState(): boolean {
-    return isNullOrUndefined(this?.bootNotificationResponse?.status);
+  public inUnknownState (): boolean {
+    return this.bootNotificationResponse?.status == null
   }
 
-  public inPendingState(): boolean {
-    return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.PENDING;
+  public inPendingState (): boolean {
+    return this.bootNotificationResponse?.status === RegistrationStatusEnumType.PENDING
   }
 
-  public inAcceptedState(): boolean {
-    return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.ACCEPTED;
+  public inAcceptedState (): boolean {
+    return this.bootNotificationResponse?.status === RegistrationStatusEnumType.ACCEPTED
   }
 
-  public inRejectedState(): boolean {
-    return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.REJECTED;
+  public inRejectedState (): boolean {
+    return this.bootNotificationResponse?.status === RegistrationStatusEnumType.REJECTED
   }
 
-  public isRegistered(): boolean {
-    return (
-      this.inUnknownState() === false &&
-      (this.inAcceptedState() === true || this.inPendingState() === true)
-    );
+  public isRegistered (): boolean {
+    return !this.inUnknownState() && (this.inAcceptedState() || this.inPendingState())
   }
 
-  public isChargingStationAvailable(): boolean {
-    return this.getConnectorStatus(0)?.availability === AvailabilityType.Operative;
+  public isChargingStationAvailable (): boolean {
+    return this.getConnectorStatus(0)?.availability === AvailabilityType.Operative
   }
 
-  public hasConnector(connectorId: number): boolean {
+  public hasConnector (connectorId: number): boolean {
     if (this.hasEvses) {
       for (const evseStatus of this.evses.values()) {
         if (evseStatus.connectors.has(connectorId)) {
-          return true;
+          return true
         }
       }
-      return false;
+      return false
     }
-    return this.connectors.has(connectorId);
+    return this.connectors.has(connectorId)
   }
 
-  public isConnectorAvailable(connectorId: number): boolean {
+  public isConnectorAvailable (connectorId: number): boolean {
     return (
       connectorId > 0 &&
       this.getConnectorStatus(connectorId)?.availability === AvailabilityType.Operative
-    );
+    )
   }
 
-  public getNumberOfConnectors(): number {
+  public getNumberOfConnectors (): number {
     if (this.hasEvses) {
-      let numberOfConnectors = 0;
+      let numberOfConnectors = 0
       for (const [evseId, evseStatus] of this.evses) {
         if (evseId > 0) {
-          numberOfConnectors += evseStatus.connectors.size;
+          numberOfConnectors += evseStatus.connectors.size
         }
       }
-      return numberOfConnectors;
+      return numberOfConnectors
     }
-    return this.connectors.has(0) ? this.connectors.size - 1 : this.connectors.size;
+    return this.connectors.has(0) ? this.connectors.size - 1 : this.connectors.size
   }
 
-  public getNumberOfEvses(): number {
-    return this.evses.has(0) ? this.evses.size - 1 : this.evses.size;
+  public getNumberOfEvses (): number {
+    return this.evses.has(0) ? this.evses.size - 1 : this.evses.size
   }
 
-  public getConnectorStatus(connectorId: number): ConnectorStatus | undefined {
+  public getConnectorStatus (connectorId: number): ConnectorStatus | undefined {
     if (this.hasEvses) {
       for (const evseStatus of this.evses.values()) {
         if (evseStatus.connectors.has(connectorId)) {
-          return evseStatus.connectors.get(connectorId);
+          return evseStatus.connectors.get(connectorId)
         }
       }
-      return undefined;
+      return undefined
     }
-    return this.connectors.get(connectorId);
+    return this.connectors.get(connectorId)
   }
 
-  public getConnectorMaximumAvailablePower(connectorId: number): number {
-    let connectorAmperageLimitationPowerLimit: number | undefined;
+  public getConnectorMaximumAvailablePower (connectorId: number): number {
+    let connectorAmperageLimitationPowerLimit: number | undefined
+    const amperageLimitation = this.getAmperageLimitation()
     if (
-      !isNullOrUndefined(this.getAmperageLimitation()) &&
-      this.getAmperageLimitation()! < this.stationInfo.maximumAmperage!
+      amperageLimitation != null &&
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      amperageLimitation < this.stationInfo!.maximumAmperage!
     ) {
       connectorAmperageLimitationPowerLimit =
         (this.stationInfo?.currentOutType === CurrentType.AC
           ? ACElectricUtils.powerTotal(
-              this.getNumberOfPhases(),
-              this.stationInfo.voltageOut!,
-              this.getAmperageLimitation()! *
-                (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()),
-            )
-          : DCElectricUtils.power(this.stationInfo.voltageOut!, this.getAmperageLimitation()!)) /
-        this.powerDivider;
+            this.getNumberOfPhases(),
+            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+            this.stationInfo.voltageOut!,
+            amperageLimitation *
+                (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors())
+          )
+          : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          DCElectricUtils.power(this.stationInfo!.voltageOut!, amperageLimitation)) /
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        this.powerDivider!
     }
-    const connectorMaximumPower = this.stationInfo.maximumPower! / this.powerDivider;
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const connectorMaximumPower = this.stationInfo!.maximumPower! / this.powerDivider!
     const connectorChargingProfilesPowerLimit =
-      getChargingStationConnectorChargingProfilesPowerLimit(this, connectorId);
+      getChargingStationConnectorChargingProfilesPowerLimit(this, connectorId)
     return min(
       isNaN(connectorMaximumPower) ? Infinity : connectorMaximumPower,
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       isNaN(connectorAmperageLimitationPowerLimit!)
         ? Infinity
-        : connectorAmperageLimitationPowerLimit!,
-      isNaN(connectorChargingProfilesPowerLimit!) ? Infinity : connectorChargingProfilesPowerLimit!,
-    );
+        : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        connectorAmperageLimitationPowerLimit!,
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      isNaN(connectorChargingProfilesPowerLimit!) ? Infinity : connectorChargingProfilesPowerLimit!
+    )
   }
 
-  public getTransactionIdTag(transactionId: number): string | undefined {
+  public getTransactionIdTag (transactionId: number): string | undefined {
     if (this.hasEvses) {
       for (const evseStatus of this.evses.values()) {
         for (const connectorStatus of evseStatus.connectors.values()) {
           if (connectorStatus.transactionId === transactionId) {
-            return connectorStatus.transactionIdTag;
+            return connectorStatus.transactionIdTag
           }
         }
       }
     } else {
       for (const connectorId of this.connectors.keys()) {
         if (this.getConnectorStatus(connectorId)?.transactionId === transactionId) {
-          return this.getConnectorStatus(connectorId)?.transactionIdTag;
+          return this.getConnectorStatus(connectorId)?.transactionIdTag
         }
       }
     }
   }
 
-  public getNumberOfRunningTransactions(): number {
-    let numberOfRunningTransactions = 0;
+  public getNumberOfRunningTransactions (): number {
+    let numberOfRunningTransactions = 0
     if (this.hasEvses) {
       for (const [evseId, evseStatus] of this.evses) {
         if (evseId === 0) {
-          continue;
+          continue
         }
         for (const connectorStatus of evseStatus.connectors.values()) {
           if (connectorStatus.transactionStarted === true) {
-            ++numberOfRunningTransactions;
+            ++numberOfRunningTransactions
           }
         }
       }
     } else {
       for (const connectorId of this.connectors.keys()) {
         if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) {
-          ++numberOfRunningTransactions;
+          ++numberOfRunningTransactions
         }
       }
     }
-    return numberOfRunningTransactions;
+    return numberOfRunningTransactions
   }
 
-  public getConnectorIdByTransactionId(transactionId: number): number | undefined {
-    if (this.hasEvses) {
+  public getConnectorIdByTransactionId (transactionId: number | undefined): number | undefined {
+    if (transactionId == null) {
+      return undefined
+    } else if (this.hasEvses) {
       for (const evseStatus of this.evses.values()) {
         for (const [connectorId, connectorStatus] of evseStatus.connectors) {
           if (connectorStatus.transactionId === transactionId) {
-            return connectorId;
+            return connectorId
           }
         }
       }
     } else {
       for (const connectorId of this.connectors.keys()) {
         if (this.getConnectorStatus(connectorId)?.transactionId === transactionId) {
-          return connectorId;
+          return connectorId
         }
       }
     }
   }
 
-  public getEnergyActiveImportRegisterByTransactionId(
-    transactionId: number,
-    rounded = false,
+  public getEnergyActiveImportRegisterByTransactionId (
+    transactionId: number | undefined,
+    rounded = false
   ): number {
     return this.getEnergyActiveImportRegister(
-      this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)!)!,
-      rounded,
-    );
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)!),
+      rounded
+    )
   }
 
-  public getEnergyActiveImportRegisterByConnectorId(connectorId: number, rounded = false): number {
-    return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId)!, rounded);
+  public getEnergyActiveImportRegisterByConnectorId (connectorId: number, rounded = false): number {
+    return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId), rounded)
   }
 
-  public getAuthorizeRemoteTxRequests(): boolean {
+  public getAuthorizeRemoteTxRequests (): boolean {
     const authorizeRemoteTxRequests = getConfigurationKey(
       this,
-      StandardParametersKey.AuthorizeRemoteTxRequests,
-    );
-    return authorizeRemoteTxRequests !== undefined
+      StandardParametersKey.AuthorizeRemoteTxRequests
+    )
+    return authorizeRemoteTxRequests != null
       ? convertToBoolean(authorizeRemoteTxRequests.value)
-      : false;
+      : false
   }
 
-  public getLocalAuthListEnabled(): boolean {
+  public getLocalAuthListEnabled (): boolean {
     const localAuthListEnabled = getConfigurationKey(
       this,
-      StandardParametersKey.LocalAuthListEnabled,
-    );
-    return localAuthListEnabled !== undefined
-      ? convertToBoolean(localAuthListEnabled.value)
-      : false;
+      StandardParametersKey.LocalAuthListEnabled
+    )
+    return localAuthListEnabled != null ? convertToBoolean(localAuthListEnabled.value) : false
   }
 
-  public getHeartbeatInterval(): number {
-    const HeartbeatInterval = getConfigurationKey(this, StandardParametersKey.HeartbeatInterval);
-    if (HeartbeatInterval !== undefined) {
-      return secondsToMilliseconds(convertToInt(HeartbeatInterval.value));
+  public getHeartbeatInterval (): number {
+    const HeartbeatInterval = getConfigurationKey(this, StandardParametersKey.HeartbeatInterval)
+    if (HeartbeatInterval != null) {
+      return secondsToMilliseconds(convertToInt(HeartbeatInterval.value))
     }
-    const HeartBeatInterval = getConfigurationKey(this, StandardParametersKey.HeartBeatInterval);
-    if (HeartBeatInterval !== undefined) {
-      return secondsToMilliseconds(convertToInt(HeartBeatInterval.value));
+    const HeartBeatInterval = getConfigurationKey(this, StandardParametersKey.HeartBeatInterval)
+    if (HeartBeatInterval != null) {
+      return secondsToMilliseconds(convertToInt(HeartBeatInterval.value))
     }
     this.stationInfo?.autoRegister === false &&
       logger.warn(
         `${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${
           Constants.DEFAULT_HEARTBEAT_INTERVAL
-        }`,
-      );
-    return Constants.DEFAULT_HEARTBEAT_INTERVAL;
+        }`
+      )
+    return Constants.DEFAULT_HEARTBEAT_INTERVAL
   }
 
-  public setSupervisionUrl(url: string): void {
+  public setSupervisionUrl (url: string): void {
     if (
       this.stationInfo?.supervisionUrlOcppConfiguration === true &&
-      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey)
+      isNotEmptyString(this.stationInfo.supervisionUrlOcppKey)
     ) {
-      setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey!, url);
+      setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey, url)
     } else {
-      this.stationInfo.supervisionUrls = url;
-      this.saveStationInfo();
-      this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl();
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      this.stationInfo!.supervisionUrls = url
+      this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl()
+      this.saveStationInfo()
     }
   }
 
-  public startHeartbeat(): void {
-    if (this.getHeartbeatInterval() > 0 && this.heartbeatSetInterval === undefined) {
+  public startHeartbeat (): void {
+    if (this.getHeartbeatInterval() > 0 && this.heartbeatSetInterval == null) {
       this.heartbeatSetInterval = setInterval(() => {
         this.ocppRequestService
           .requestHandler<HeartbeatRequest, HeartbeatResponse>(this, RequestCommand.HEARTBEAT)
-          .catch((error) => {
+          .catch((error: unknown) => {
             logger.error(
               `${this.logPrefix()} Error while sending '${RequestCommand.HEARTBEAT}':`,
-              error,
-            );
-          });
-      }, this.getHeartbeatInterval());
+              error
+            )
+          })
+      }, this.getHeartbeatInterval())
       logger.info(
         `${this.logPrefix()} Heartbeat started every ${formatDurationMilliSeconds(
-          this.getHeartbeatInterval(),
-        )}`,
-      );
-    } else if (this.heartbeatSetInterval !== undefined) {
+          this.getHeartbeatInterval()
+        )}`
+      )
+    } else if (this.heartbeatSetInterval != null) {
       logger.info(
         `${this.logPrefix()} Heartbeat already started every ${formatDurationMilliSeconds(
-          this.getHeartbeatInterval(),
-        )}`,
-      );
+          this.getHeartbeatInterval()
+        )}`
+      )
     } else {
       logger.error(
-        `${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval()}, not starting the heartbeat`,
-      );
+        `${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval()}, not starting the heartbeat`
+      )
     }
   }
 
-  public restartHeartbeat(): void {
+  public restartHeartbeat (): void {
     // Stop heartbeat
-    this.stopHeartbeat();
+    this.stopHeartbeat()
     // Start heartbeat
-    this.startHeartbeat();
+    this.startHeartbeat()
   }
 
-  public restartWebSocketPing(): void {
+  public restartWebSocketPing (): void {
     // Stop WebSocket ping
-    this.stopWebSocketPing();
+    this.stopWebSocketPing()
     // Start WebSocket ping
-    this.startWebSocketPing();
+    this.startWebSocketPing()
   }
 
-  public startMeterValues(connectorId: number, interval: number): void {
+  public startMeterValues (connectorId: number, interval: number): void {
     if (connectorId === 0) {
-      logger.error(
-        `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId}`,
-      );
-      return;
+      logger.error(`${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId}`)
+      return
     }
-    if (!this.getConnectorStatus(connectorId)) {
+    const connectorStatus = this.getConnectorStatus(connectorId)
+    if (connectorStatus == null) {
       logger.error(
         `${this.logPrefix()} Trying to start MeterValues on non existing connector id
-          ${connectorId}`,
-      );
-      return;
+          ${connectorId}`
+      )
+      return
     }
-    if (this.getConnectorStatus(connectorId)?.transactionStarted === false) {
+    if (connectorStatus.transactionStarted === false) {
       logger.error(
-        `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction started`,
-      );
-      return;
+        `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction started`
+      )
+      return
     } else if (
-      this.getConnectorStatus(connectorId)?.transactionStarted === true &&
-      isNullOrUndefined(this.getConnectorStatus(connectorId)?.transactionId)
+      connectorStatus.transactionStarted === true &&
+      connectorStatus.transactionId == null
     ) {
       logger.error(
-        `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id`,
-      );
-      return;
+        `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id`
+      )
+      return
     }
     if (interval > 0) {
-      this.getConnectorStatus(connectorId)!.transactionSetInterval = setInterval(() => {
-        // FIXME: Implement OCPP version agnostic helpers
-        const meterValue: MeterValue = OCPP16ServiceUtils.buildMeterValue(
+      connectorStatus.transactionSetInterval = setInterval(() => {
+        const meterValue = buildMeterValue(
           this,
           connectorId,
-          this.getConnectorStatus(connectorId)!.transactionId!,
-          interval,
-        );
+          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          connectorStatus.transactionId!,
+          interval
+        )
         this.ocppRequestService
           .requestHandler<MeterValuesRequest, MeterValuesResponse>(
-            this,
-            RequestCommand.METER_VALUES,
-            {
-              connectorId,
-              transactionId: this.getConnectorStatus(connectorId)?.transactionId,
-              meterValue: [meterValue],
-            },
-          )
-          .catch((error) => {
+          this,
+          RequestCommand.METER_VALUES,
+          {
+            connectorId,
+            transactionId: connectorStatus.transactionId,
+            meterValue: [meterValue]
+          }
+        )
+          .catch((error: unknown) => {
             logger.error(
               `${this.logPrefix()} Error while sending '${RequestCommand.METER_VALUES}':`,
-              error,
-            );
-          });
-      }, interval);
+              error
+            )
+          })
+      }, interval)
     } else {
       logger.error(
         `${this.logPrefix()} Charging station ${
           StandardParametersKey.MeterValueSampleInterval
-        } configuration set to ${interval}, not sending MeterValues`,
-      );
+        } configuration set to ${interval}, not sending MeterValues`
+      )
     }
   }
 
-  public stopMeterValues(connectorId: number) {
-    if (this.getConnectorStatus(connectorId)?.transactionSetInterval !== undefined) {
-      clearInterval(this.getConnectorStatus(connectorId)?.transactionSetInterval);
+  public stopMeterValues (connectorId: number): void {
+    const connectorStatus = this.getConnectorStatus(connectorId)
+    if (connectorStatus?.transactionSetInterval != null) {
+      clearInterval(connectorStatus.transactionSetInterval)
     }
   }
 
-  public start(): void {
-    if (this.started === false) {
-      if (this.starting === false) {
-        this.starting = true;
+  private add (): void {
+    this.emit(ChargingStationEvents.added)
+  }
+
+  public async delete (deleteConfiguration = true): Promise<void> {
+    if (this.started) {
+      await this.stop()
+    }
+    AutomaticTransactionGenerator.deleteInstance(this)
+    PerformanceStatistics.deleteInstance(this.stationInfo?.hashId)
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    this.idTagsCache.deleteIdTags(getIdTagsFile(this.stationInfo!)!)
+    this.requests.clear()
+    this.connectors.clear()
+    this.evses.clear()
+    this.templateFileWatcher?.unref()
+    deleteConfiguration && rmSync(this.configurationFile, { force: true })
+    this.chargingStationWorkerBroadcastChannel.unref()
+    this.emit(ChargingStationEvents.deleted)
+    this.removeAllListeners()
+  }
+
+  public start (): void {
+    if (!this.started) {
+      if (!this.starting) {
+        this.starting = true
         if (this.stationInfo?.enableStatistics === true) {
-          this.performanceStatistics?.start();
+          this.performanceStatistics?.start()
         }
-        this.openWSConnection();
+        this.openWSConnection()
         // Monitor charging station template file
         this.templateFileWatcher = watchJsonFile(
           this.templateFile,
@@ -630,1033 +703,1030 @@ export class ChargingStation extends EventEmitter {
                 logger.debug(
                   `${this.logPrefix()} ${FileType.ChargingStationTemplate} ${
                     this.templateFile
-                  } file have changed, reload`,
-                );
-                this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash);
+                  } file have changed, reload`
+                )
+                this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash)
+                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+                this.idTagsCache.deleteIdTags(getIdTagsFile(this.stationInfo!)!)
                 // Initialize
-                this.initialize();
-                this.idTagsCache.deleteIdTags(getIdTagsFile(this.stationInfo)!);
+                this.initialize()
                 // Restart the ATG
-                this.stopAutomaticTransactionGenerator();
-                delete this.automaticTransactionGeneratorConfiguration;
-                if (this.getAutomaticTransactionGeneratorConfiguration().enable === true) {
-                  this.startAutomaticTransactionGenerator();
+                const ATGStarted = this.automaticTransactionGenerator?.started
+                if (ATGStarted === true) {
+                  this.stopAutomaticTransactionGenerator()
+                }
+                delete this.automaticTransactionGeneratorConfiguration
+                if (
+                  this.getAutomaticTransactionGeneratorConfiguration()?.enable === true &&
+                  ATGStarted === true
+                ) {
+                  this.startAutomaticTransactionGenerator(undefined, true)
                 }
                 if (this.stationInfo?.enableStatistics === true) {
-                  this.performanceStatistics?.restart();
+                  this.performanceStatistics?.restart()
                 } else {
-                  this.performanceStatistics?.stop();
+                  this.performanceStatistics?.stop()
                 }
                 // FIXME?: restart heartbeat and WebSocket ping when their interval values have changed
               } catch (error) {
                 logger.error(
                   `${this.logPrefix()} ${FileType.ChargingStationTemplate} file monitoring error:`,
-                  error,
-                );
+                  error
+                )
               }
             }
-          },
-        );
-        this.started = true;
-        this.emit(ChargingStationEvents.started);
-        this.starting = false;
+          }
+        )
+        this.started = true
+        this.emit(ChargingStationEvents.started)
+        this.starting = false
       } else {
-        logger.warn(`${this.logPrefix()} Charging station is already starting...`);
+        logger.warn(`${this.logPrefix()} Charging station is already starting...`)
       }
     } else {
-      logger.warn(`${this.logPrefix()} Charging station is already started...`);
+      logger.warn(`${this.logPrefix()} Charging station is already started...`)
     }
   }
 
-  public async stop(reason?: StopTransactionReason, stopTransactions?: boolean): Promise<void> {
-    if (this.started === true) {
-      if (this.stopping === false) {
-        this.stopping = true;
-        await this.stopMessageSequence(reason, stopTransactions);
-        this.closeWSConnection();
+  public async stop (
+    reason?: StopTransactionReason,
+    stopTransactions = this.stationInfo?.stopTransactionsOnStopped
+  ): Promise<void> {
+    if (this.started) {
+      if (!this.stopping) {
+        this.stopping = true
+        await this.stopMessageSequence(reason, stopTransactions)
+        this.closeWSConnection()
         if (this.stationInfo?.enableStatistics === true) {
-          this.performanceStatistics?.stop();
+          this.performanceStatistics?.stop()
         }
-        this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash);
-        this.templateFileWatcher?.close();
-        this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash);
-        delete this.bootNotificationResponse;
-        this.started = false;
-        this.saveConfiguration();
-        this.emit(ChargingStationEvents.stopped);
-        this.stopping = false;
+        this.templateFileWatcher?.close()
+        delete this.bootNotificationResponse
+        this.started = false
+        this.saveConfiguration()
+        this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash)
+        this.emit(ChargingStationEvents.stopped)
+        this.stopping = false
       } else {
-        logger.warn(`${this.logPrefix()} Charging station is already stopping...`);
+        logger.warn(`${this.logPrefix()} Charging station is already stopping...`)
       }
     } else {
-      logger.warn(`${this.logPrefix()} Charging station is already stopped...`);
+      logger.warn(`${this.logPrefix()} Charging station is already stopped...`)
     }
   }
 
-  public async reset(reason?: StopTransactionReason): Promise<void> {
-    await this.stop(reason);
-    await sleep(this.stationInfo.resetTime!);
-    this.initialize();
-    this.start();
+  public async reset (reason?: StopTransactionReason): Promise<void> {
+    await this.stop(reason)
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    await sleep(this.stationInfo!.resetTime!)
+    this.initialize()
+    this.start()
   }
 
-  public saveOcppConfiguration(): void {
+  public saveOcppConfiguration (): void {
     if (this.stationInfo?.ocppPersistentConfiguration === true) {
-      this.saveConfiguration();
+      this.saveConfiguration()
     }
   }
 
-  public bufferMessage(message: string): void {
-    this.messageBuffer.add(message);
-    this.setIntervalFlushMessageBuffer();
+  public bufferMessage (message: string): void {
+    this.messageBuffer.add(message)
+    this.setIntervalFlushMessageBuffer()
   }
 
-  public openWSConnection(
+  public openWSConnection (
     options?: WsOptions,
-    params?: { closeOpened?: boolean; terminateOpened?: boolean },
+    params?: { closeOpened?: boolean, terminateOpened?: boolean }
   ): void {
     options = {
       handshakeTimeout: secondsToMilliseconds(this.getConnectionTimeout()),
       ...this.stationInfo?.wsOptions,
-      ...options,
-    };
-    params = { ...{ closeOpened: false, terminateOpened: false }, ...params };
+      ...options
+    }
+    params = { ...{ closeOpened: false, terminateOpened: false }, ...params }
     if (!checkChargingStation(this, this.logPrefix())) {
-      return;
+      return
     }
-    if (
-      !isNullOrUndefined(this.stationInfo.supervisionUser) &&
-      !isNullOrUndefined(this.stationInfo.supervisionPassword)
-    ) {
-      options.auth = `${this.stationInfo.supervisionUser}:${this.stationInfo.supervisionPassword}`;
+    if (this.stationInfo?.supervisionUser != null && this.stationInfo.supervisionPassword != null) {
+      options.auth = `${this.stationInfo.supervisionUser}:${this.stationInfo.supervisionPassword}`
     }
-    if (params?.closeOpened) {
-      this.closeWSConnection();
+    if (params.closeOpened === true) {
+      this.closeWSConnection()
     }
-    if (params?.terminateOpened) {
-      this.terminateWSConnection();
+    if (params.terminateOpened === true) {
+      this.terminateWSConnection()
     }
 
-    if (this.isWebSocketConnectionOpened() === true) {
+    if (this.isWebSocketConnectionOpened()) {
       logger.warn(
-        `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} is already opened`,
-      );
-      return;
+        `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.href} is already opened`
+      )
+      return
     }
 
-    logger.info(
-      `${this.logPrefix()} Open OCPP connection to URL ${this.wsConnectionUrl.toString()}`,
-    );
+    logger.info(`${this.logPrefix()} Open OCPP connection to URL ${this.wsConnectionUrl.href}`)
 
     this.wsConnection = new WebSocket(
       this.wsConnectionUrl,
       `ocpp${this.stationInfo?.ocppVersion}`,
-      options,
-    );
+      options
+    )
 
     // Handle WebSocket message
-    this.wsConnection.on(
-      'message',
-      this.onMessage.bind(this) as (this: WebSocket, data: RawData, isBinary: boolean) => void,
-    );
+    this.wsConnection.on('message', data => {
+      this.onMessage(data).catch(Constants.EMPTY_FUNCTION)
+    })
     // Handle WebSocket error
-    this.wsConnection.on(
-      'error',
-      this.onError.bind(this) as (this: WebSocket, error: Error) => void,
-    );
+    this.wsConnection.on('error', this.onError.bind(this))
     // Handle WebSocket close
-    this.wsConnection.on(
-      'close',
-      this.onClose.bind(this) as (this: WebSocket, code: number, reason: Buffer) => void,
-    );
+    this.wsConnection.on('close', this.onClose.bind(this))
     // Handle WebSocket open
-    this.wsConnection.on('open', this.onOpen.bind(this) as (this: WebSocket) => void);
+    this.wsConnection.on('open', () => {
+      this.onOpen().catch((error: unknown) =>
+        logger.error(`${this.logPrefix()} Error while opening WebSocket connection:`, error)
+      )
+    })
     // Handle WebSocket ping
-    this.wsConnection.on('ping', this.onPing.bind(this) as (this: WebSocket, data: Buffer) => void);
+    this.wsConnection.on('ping', this.onPing.bind(this))
     // Handle WebSocket pong
-    this.wsConnection.on('pong', this.onPong.bind(this) as (this: WebSocket, data: Buffer) => void);
+    this.wsConnection.on('pong', this.onPong.bind(this))
   }
 
-  public closeWSConnection(): void {
-    if (this.isWebSocketConnectionOpened() === true) {
-      this.wsConnection?.close();
-      this.wsConnection = null;
+  public closeWSConnection (): void {
+    if (this.isWebSocketConnectionOpened()) {
+      this.wsConnection?.close()
+      this.wsConnection = null
     }
   }
 
-  public getAutomaticTransactionGeneratorConfiguration(): AutomaticTransactionGeneratorConfiguration {
-    if (isNullOrUndefined(this.automaticTransactionGeneratorConfiguration)) {
+  public getAutomaticTransactionGeneratorConfiguration ():
+  | AutomaticTransactionGeneratorConfiguration
+  | undefined {
+    if (this.automaticTransactionGeneratorConfiguration == null) {
       let automaticTransactionGeneratorConfiguration:
-        | AutomaticTransactionGeneratorConfiguration
-        | undefined;
-      const stationTemplate = this.getTemplateFromFile();
-      const stationConfiguration = this.getConfigurationFromFile();
+      | AutomaticTransactionGeneratorConfiguration
+      | undefined
+      const stationTemplate = this.getTemplateFromFile()
+      const stationConfiguration = this.getConfigurationFromFile()
       if (
         this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration === true &&
         stationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash &&
-        stationConfiguration?.automaticTransactionGenerator
+        stationConfiguration?.automaticTransactionGenerator != null
       ) {
         automaticTransactionGeneratorConfiguration =
-          stationConfiguration?.automaticTransactionGenerator;
+          stationConfiguration.automaticTransactionGenerator
       } else {
-        automaticTransactionGeneratorConfiguration = stationTemplate?.AutomaticTransactionGenerator;
+        automaticTransactionGeneratorConfiguration = stationTemplate?.AutomaticTransactionGenerator
       }
       this.automaticTransactionGeneratorConfiguration = {
         ...Constants.DEFAULT_ATG_CONFIGURATION,
-        ...automaticTransactionGeneratorConfiguration,
-      };
+        ...automaticTransactionGeneratorConfiguration
+      }
     }
-    return this.automaticTransactionGeneratorConfiguration!;
+    return this.automaticTransactionGeneratorConfiguration
   }
 
-  public getAutomaticTransactionGeneratorStatuses(): Status[] | undefined {
-    return this.getConfigurationFromFile()?.automaticTransactionGeneratorStatuses;
+  public getAutomaticTransactionGeneratorStatuses (): Status[] | undefined {
+    return this.getConfigurationFromFile()?.automaticTransactionGeneratorStatuses
   }
 
-  public startAutomaticTransactionGenerator(connectorIds?: number[]): void {
-    this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this);
+  public startAutomaticTransactionGenerator (
+    connectorIds?: number[],
+    stopAbsoluteDuration?: boolean
+  ): void {
+    this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this)
     if (isNotEmptyArray(connectorIds)) {
-      for (const connectorId of connectorIds!) {
-        this.automaticTransactionGenerator?.startConnector(connectorId);
+      for (const connectorId of connectorIds) {
+        this.automaticTransactionGenerator?.startConnector(connectorId, stopAbsoluteDuration)
       }
     } else {
-      this.automaticTransactionGenerator?.start();
+      this.automaticTransactionGenerator?.start(stopAbsoluteDuration)
     }
-    this.saveAutomaticTransactionGeneratorConfiguration();
-    this.emit(ChargingStationEvents.updated);
+    this.saveAutomaticTransactionGeneratorConfiguration()
+    this.emit(ChargingStationEvents.updated)
   }
 
-  public stopAutomaticTransactionGenerator(connectorIds?: number[]): void {
+  public stopAutomaticTransactionGenerator (connectorIds?: number[]): void {
     if (isNotEmptyArray(connectorIds)) {
-      for (const connectorId of connectorIds!) {
-        this.automaticTransactionGenerator?.stopConnector(connectorId);
+      for (const connectorId of connectorIds) {
+        this.automaticTransactionGenerator?.stopConnector(connectorId)
       }
     } else {
-      this.automaticTransactionGenerator?.stop();
+      this.automaticTransactionGenerator?.stop()
     }
-    this.saveAutomaticTransactionGeneratorConfiguration();
-    this.emit(ChargingStationEvents.updated);
+    this.saveAutomaticTransactionGeneratorConfiguration()
+    this.emit(ChargingStationEvents.updated)
   }
 
-  public async stopTransactionOnConnector(
+  public async stopTransactionOnConnector (
     connectorId: number,
-    reason?: StopTransactionReason,
+    reason?: StopTransactionReason
   ): Promise<StopTransactionResponse> {
-    const transactionId = this.getConnectorStatus(connectorId)?.transactionId;
+    const transactionId = this.getConnectorStatus(connectorId)?.transactionId
     if (
       this.stationInfo?.beginEndMeterValues === true &&
-      this.stationInfo?.ocppStrictCompliance === true &&
-      this.stationInfo?.outOfOrderEndMeterValues === false
+      this.stationInfo.ocppStrictCompliance === true &&
+      this.stationInfo.outOfOrderEndMeterValues === false
     ) {
-      // FIXME: Implement OCPP version agnostic helpers
-      const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
+      const transactionEndMeterValue = buildTransactionEndMeterValue(
         this,
         connectorId,
-        this.getEnergyActiveImportRegisterByTransactionId(transactionId!),
-      );
+        this.getEnergyActiveImportRegisterByTransactionId(transactionId)
+      )
       await this.ocppRequestService.requestHandler<MeterValuesRequest, MeterValuesResponse>(
         this,
         RequestCommand.METER_VALUES,
         {
           connectorId,
           transactionId,
-          meterValue: [transactionEndMeterValue],
-        },
-      );
+          meterValue: [transactionEndMeterValue]
+        }
+      )
     }
-    return this.ocppRequestService.requestHandler<StopTransactionRequest, StopTransactionResponse>(
-      this,
-      RequestCommand.STOP_TRANSACTION,
-      {
-        transactionId,
-        meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId!, true),
-        ...(isNullOrUndefined(reason) && { reason }),
-      },
-    );
+    return await this.ocppRequestService.requestHandler<
+    Partial<StopTransactionRequest>,
+    StopTransactionResponse
+    >(this, RequestCommand.STOP_TRANSACTION, {
+      transactionId,
+      meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId, true),
+      ...(reason != null && { reason })
+    })
   }
 
-  public getReserveConnectorZeroSupported(): boolean {
+  public getReserveConnectorZeroSupported (): boolean {
     return convertToBoolean(
-      getConfigurationKey(this, StandardParametersKey.ReserveConnectorZeroSupported)!.value,
-    );
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      getConfigurationKey(this, StandardParametersKey.ReserveConnectorZeroSupported)!.value
+    )
   }
 
-  public async addReservation(reservation: Reservation): Promise<void> {
-    const reservationFound = this.getReservationBy('reservationId', reservation.reservationId);
-    if (reservationFound !== undefined) {
-      await this.removeReservation(reservationFound, ReservationTerminationReason.REPLACE_EXISTING);
+  public async addReservation (reservation: Reservation): Promise<void> {
+    const reservationFound = this.getReservationBy('reservationId', reservation.reservationId)
+    if (reservationFound != null) {
+      await this.removeReservation(reservationFound, ReservationTerminationReason.REPLACE_EXISTING)
     }
-    this.getConnectorStatus(reservation.connectorId)!.reservation = reservation;
-    await OCPPServiceUtils.sendAndSetConnectorStatus(
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    this.getConnectorStatus(reservation.connectorId)!.reservation = reservation
+    await sendAndSetConnectorStatus(
       this,
       reservation.connectorId,
       ConnectorStatusEnum.Reserved,
       undefined,
-      { send: reservation.connectorId !== 0 },
-    );
+      { send: reservation.connectorId !== 0 }
+    )
   }
 
-  public async removeReservation(
+  public async removeReservation (
     reservation: Reservation,
-    reason: ReservationTerminationReason,
+    reason: ReservationTerminationReason
   ): Promise<void> {
-    const connector = this.getConnectorStatus(reservation.connectorId)!;
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const connector = this.getConnectorStatus(reservation.connectorId)!
     switch (reason) {
       case ReservationTerminationReason.CONNECTOR_STATE_CHANGED:
       case ReservationTerminationReason.TRANSACTION_STARTED:
-        delete connector.reservation;
-        break;
+        delete connector.reservation
+        break
       case ReservationTerminationReason.RESERVATION_CANCELED:
       case ReservationTerminationReason.REPLACE_EXISTING:
       case ReservationTerminationReason.EXPIRED:
-        await OCPPServiceUtils.sendAndSetConnectorStatus(
+        await sendAndSetConnectorStatus(
           this,
           reservation.connectorId,
           ConnectorStatusEnum.Available,
           undefined,
-          { send: reservation.connectorId !== 0 },
-        );
-        delete connector.reservation;
-        break;
+          { send: reservation.connectorId !== 0 }
+        )
+        delete connector.reservation
+        break
       default:
         // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-        throw new BaseError(`Unknown reservation termination reason '${reason}'`);
+        throw new BaseError(`Unknown reservation termination reason '${reason}'`)
     }
   }
 
-  public getReservationBy(
+  public getReservationBy (
     filterKey: ReservationKey,
-    value: number | string,
+    value: number | string
   ): Reservation | undefined {
     if (this.hasEvses) {
       for (const evseStatus of this.evses.values()) {
         for (const connectorStatus of evseStatus.connectors.values()) {
-          if (connectorStatus?.reservation?.[filterKey] === value) {
-            return connectorStatus.reservation;
+          if (connectorStatus.reservation?.[filterKey] === value) {
+            return connectorStatus.reservation
           }
         }
       }
     } else {
       for (const connectorStatus of this.connectors.values()) {
-        if (connectorStatus?.reservation?.[filterKey] === value) {
-          return connectorStatus.reservation;
+        if (connectorStatus.reservation?.[filterKey] === value) {
+          return connectorStatus.reservation
         }
       }
     }
   }
 
-  public isConnectorReservable(
+  public isConnectorReservable (
     reservationId: number,
     idTag?: string,
-    connectorId?: number,
+    connectorId?: number
   ): boolean {
-    const reservation = this.getReservationBy('reservationId', reservationId);
-    const reservationExists = !isUndefined(reservation) && !hasReservationExpired(reservation!);
+    const reservation = this.getReservationBy('reservationId', reservationId)
+    const reservationExists = reservation != null && !hasReservationExpired(reservation)
     if (arguments.length === 1) {
-      return !reservationExists;
+      return !reservationExists
     } else if (arguments.length > 1) {
-      const userReservation = !isUndefined(idTag)
-        ? this.getReservationBy('idTag', idTag!)
-        : undefined;
+      const userReservation = idTag != null ? this.getReservationBy('idTag', idTag) : undefined
       const userReservationExists =
-        !isUndefined(userReservation) && !hasReservationExpired(userReservation!);
-      const notConnectorZero = isUndefined(connectorId) ? true : connectorId! > 0;
-      const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0;
+        userReservation != null && !hasReservationExpired(userReservation)
+      const notConnectorZero = connectorId == null ? true : connectorId > 0
+      const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0
       return (
         !reservationExists && !userReservationExists && notConnectorZero && freeConnectorsAvailable
-      );
+      )
     }
-    return false;
+    return false
   }
 
-  private setIntervalFlushMessageBuffer(): void {
-    if (this.flushMessageBufferSetInterval === undefined) {
+  private setIntervalFlushMessageBuffer (): void {
+    if (this.flushMessageBufferSetInterval == null) {
       this.flushMessageBufferSetInterval = setInterval(() => {
-        if (this.isWebSocketConnectionOpened() === true && this.inAcceptedState() === true) {
-          this.flushMessageBuffer();
+        if (this.isWebSocketConnectionOpened() && this.inAcceptedState()) {
+          this.flushMessageBuffer()
         }
         if (this.messageBuffer.size === 0) {
-          this.clearIntervalFlushMessageBuffer();
+          this.clearIntervalFlushMessageBuffer()
         }
-      }, Constants.DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL);
+      }, Constants.DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL)
     }
   }
 
-  private clearIntervalFlushMessageBuffer() {
-    if (this.flushMessageBufferSetInterval !== undefined) {
-      clearInterval(this.flushMessageBufferSetInterval);
-      delete this.flushMessageBufferSetInterval;
+  private clearIntervalFlushMessageBuffer (): void {
+    if (this.flushMessageBufferSetInterval != null) {
+      clearInterval(this.flushMessageBufferSetInterval)
+      delete this.flushMessageBufferSetInterval
     }
   }
 
-  private getNumberOfReservableConnectors(): number {
-    let numberOfReservableConnectors = 0;
+  private getNumberOfReservableConnectors (): number {
+    let numberOfReservableConnectors = 0
     if (this.hasEvses) {
       for (const evseStatus of this.evses.values()) {
-        numberOfReservableConnectors += getNumberOfReservableConnectors(evseStatus.connectors);
+        numberOfReservableConnectors += getNumberOfReservableConnectors(evseStatus.connectors)
       }
     } else {
-      numberOfReservableConnectors = getNumberOfReservableConnectors(this.connectors);
+      numberOfReservableConnectors = getNumberOfReservableConnectors(this.connectors)
     }
-    return numberOfReservableConnectors - this.getNumberOfReservationsOnConnectorZero();
+    return numberOfReservableConnectors - this.getNumberOfReservationsOnConnectorZero()
   }
 
-  private getNumberOfReservationsOnConnectorZero(): number {
+  private getNumberOfReservationsOnConnectorZero (): number {
     if (
-      // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-      (this.hasEvses && this.evses.get(0)?.connectors.get(0)?.reservation) ||
-      (!this.hasEvses && this.connectors.get(0)?.reservation)
+      (this.hasEvses && this.evses.get(0)?.connectors.get(0)?.reservation != null) ||
+      (!this.hasEvses && this.connectors.get(0)?.reservation != null)
     ) {
-      return 1;
+      return 1
     }
-    return 0;
+    return 0
   }
 
-  private flushMessageBuffer(): void {
+  private flushMessageBuffer (): void {
     if (this.messageBuffer.size > 0) {
       for (const message of this.messageBuffer.values()) {
-        let beginId: string | undefined;
-        let commandName: RequestCommand | undefined;
-        const [messageType] = JSON.parse(message) as OutgoingRequest | Response | ErrorResponse;
-        const isRequest = messageType === MessageType.CALL_MESSAGE;
+        let beginId: string | undefined
+        let commandName: RequestCommand | undefined
+        const [messageType] = JSON.parse(message) as OutgoingRequest | Response | ErrorResponse
+        const isRequest = messageType === MessageType.CALL_MESSAGE
         if (isRequest) {
-          [, , commandName] = JSON.parse(message) as OutgoingRequest;
-          beginId = PerformanceStatistics.beginMeasure(commandName);
+          [, , commandName] = JSON.parse(message) as OutgoingRequest
+          beginId = PerformanceStatistics.beginMeasure(commandName)
         }
         this.wsConnection?.send(message, (error?: Error) => {
-          isRequest && PerformanceStatistics.endMeasure(commandName!, beginId!);
-          if (isNullOrUndefined(error)) {
+          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          isRequest && PerformanceStatistics.endMeasure(commandName!, beginId!)
+          if (error == null) {
             logger.debug(
-              `${this.logPrefix()} >> Buffered ${OCPPServiceUtils.getMessageTypeString(
-                messageType,
-              )} payload sent: ${message}`,
-            );
-            this.messageBuffer.delete(message);
+              `${this.logPrefix()} >> Buffered ${getMessageTypeString(
+                messageType
+              )} OCPP message sent '${JSON.stringify(message)}'`
+            )
+            this.messageBuffer.delete(message)
+          } else {
+            logger.debug(
+              `${this.logPrefix()} >> Buffered ${getMessageTypeString(
+                messageType
+              )} OCPP message '${JSON.stringify(message)}' send failed:`,
+              error
+            )
           }
-        });
+        })
       }
     }
   }
 
-  private getTemplateFromFile(): ChargingStationTemplate | undefined {
-    let template: ChargingStationTemplate | undefined;
+  private getTemplateFromFile (): ChargingStationTemplate | undefined {
+    let template: ChargingStationTemplate | undefined
     try {
       if (this.sharedLRUCache.hasChargingStationTemplate(this.templateFileHash)) {
-        template = this.sharedLRUCache.getChargingStationTemplate(this.templateFileHash);
+        template = this.sharedLRUCache.getChargingStationTemplate(this.templateFileHash)
       } else {
-        const measureId = `${FileType.ChargingStationTemplate} read`;
-        const beginId = PerformanceStatistics.beginMeasure(measureId);
-        template = JSON.parse(readFileSync(this.templateFile, 'utf8')) as ChargingStationTemplate;
-        PerformanceStatistics.endMeasure(measureId, beginId);
+        const measureId = `${FileType.ChargingStationTemplate} read`
+        const beginId = PerformanceStatistics.beginMeasure(measureId)
+        template = JSON.parse(readFileSync(this.templateFile, 'utf8')) as ChargingStationTemplate
+        PerformanceStatistics.endMeasure(measureId, beginId)
         template.templateHash = createHash(Constants.DEFAULT_HASH_ALGORITHM)
           .update(JSON.stringify(template))
-          .digest('hex');
-        this.sharedLRUCache.setChargingStationTemplate(template);
-        this.templateFileHash = template.templateHash;
+          .digest('hex')
+        this.sharedLRUCache.setChargingStationTemplate(template)
+        this.templateFileHash = template.templateHash
       }
     } catch (error) {
       handleFileException(
         this.templateFile,
         FileType.ChargingStationTemplate,
         error as NodeJS.ErrnoException,
-        this.logPrefix(),
-      );
-    }
-    return template;
-  }
-
-  private getStationInfoFromTemplate(): ChargingStationInfo {
-    const stationTemplate: ChargingStationTemplate = this.getTemplateFromFile()!;
-    checkTemplate(stationTemplate, this.logPrefix(), this.templateFile);
-    const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation, this);
-    warnTemplateKeysDeprecationOnce(stationTemplate, this.logPrefix(), this.templateFile);
-    if (stationTemplate?.Connectors) {
-      checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile);
-    }
-    const stationInfo: ChargingStationInfo = stationTemplateToStationInfo(stationTemplate);
-    stationInfo.hashId = getHashId(this.index, stationTemplate);
-    stationInfo.chargingStationId = getChargingStationId(this.index, stationTemplate);
-    stationInfo.ocppVersion = stationTemplate?.ocppVersion ?? OCPPVersion.VERSION_16;
-    createSerialNumber(stationTemplate, stationInfo);
-    stationInfo.voltageOut = this.getVoltageOut(stationInfo);
-    if (isNotEmptyArray(stationTemplate?.power)) {
-      stationTemplate.power = stationTemplate.power as number[];
-      const powerArrayRandomIndex = Math.floor(secureRandom() * stationTemplate.power.length);
+        this.logPrefix()
+      )
+    }
+    return template
+  }
+
+  private getStationInfoFromTemplate (): ChargingStationInfo {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const stationTemplate = this.getTemplateFromFile()!
+    checkTemplate(stationTemplate, this.logPrefix(), this.templateFile)
+    const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation)
+    warnTemplateKeysDeprecationOnce(stationTemplate, this.logPrefix(), this.templateFile)
+    if (stationTemplate.Connectors != null) {
+      checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile)
+    }
+    const stationInfo = stationTemplateToStationInfo(stationTemplate)
+    stationInfo.hashId = getHashId(this.index, stationTemplate)
+    stationInfo.templateIndex = this.index
+    stationInfo.templateName = buildTemplateName(this.templateFile)
+    stationInfo.chargingStationId = getChargingStationId(this.index, stationTemplate)
+    createSerialNumber(stationTemplate, stationInfo)
+    stationInfo.voltageOut = this.getVoltageOut(stationInfo)
+    if (isNotEmptyArray(stationTemplate.power)) {
+      const powerArrayRandomIndex = Math.floor(secureRandom() * stationTemplate.power.length)
       stationInfo.maximumPower =
-        stationTemplate?.powerUnit === PowerUnits.KILO_WATT
+        stationTemplate.powerUnit === PowerUnits.KILO_WATT
           ? stationTemplate.power[powerArrayRandomIndex] * 1000
-          : stationTemplate.power[powerArrayRandomIndex];
+          : stationTemplate.power[powerArrayRandomIndex]
     } else {
-      stationTemplate.power = stationTemplate?.power as number;
       stationInfo.maximumPower =
-        stationTemplate?.powerUnit === PowerUnits.KILO_WATT
-          ? stationTemplate.power * 1000
-          : stationTemplate.power;
+        stationTemplate.powerUnit === PowerUnits.KILO_WATT
+          ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          stationTemplate.power! * 1000
+          : stationTemplate.power
     }
-    stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo);
-    stationInfo.firmwareVersionPattern =
-      stationTemplate?.firmwareVersionPattern ?? Constants.SEMVER_PATTERN;
+    stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo)
     if (
+      isNotEmptyString(stationInfo.firmwareVersionPattern) &&
       isNotEmptyString(stationInfo.firmwareVersion) &&
-      new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion!) === false
+      !new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion)
     ) {
       logger.warn(
         `${this.logPrefix()} Firmware version '${stationInfo.firmwareVersion}' in template file ${
           this.templateFile
-        } does not match firmware version pattern '${stationInfo.firmwareVersionPattern}'`,
-      );
+        } does not match firmware version pattern '${stationInfo.firmwareVersionPattern}'`
+      )
     }
-    stationInfo.firmwareUpgrade = merge<FirmwareUpgrade>(
-      {
-        versionUpgrade: {
-          step: 1,
-        },
-        reset: true,
-      },
-      stationTemplate?.firmwareUpgrade ?? {},
-    );
-    stationInfo.resetTime = !isNullOrUndefined(stationTemplate?.resetTime)
-      ? secondsToMilliseconds(stationTemplate.resetTime!)
-      : Constants.CHARGING_STATION_DEFAULT_RESET_TIME;
-    return stationInfo;
-  }
-
-  private getStationInfoFromFile(
-    stationInfoPersistentConfiguration = true,
+    if (stationTemplate.resetTime != null) {
+      stationInfo.resetTime = secondsToMilliseconds(stationTemplate.resetTime)
+    }
+    return stationInfo
+  }
+
+  private getStationInfoFromFile (
+    stationInfoPersistentConfiguration: boolean | undefined = Constants.DEFAULT_STATION_INFO
+      .stationInfoPersistentConfiguration
   ): ChargingStationInfo | undefined {
-    let stationInfo: ChargingStationInfo | undefined;
+    let stationInfo: ChargingStationInfo | undefined
     if (stationInfoPersistentConfiguration === true) {
-      stationInfo = this.getConfigurationFromFile()?.stationInfo;
-      if (stationInfo) {
-        delete stationInfo?.infoHash;
+      stationInfo = this.getConfigurationFromFile()?.stationInfo
+      if (stationInfo != null) {
+        delete stationInfo.infoHash
+        delete (stationInfo as ChargingStationTemplate).numberOfConnectors
+        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+        if (stationInfo.templateIndex == null) {
+          stationInfo.templateIndex = this.index
+        }
+        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+        if (stationInfo.templateName == null) {
+          stationInfo.templateName = buildTemplateName(this.templateFile)
+        }
       }
     }
-    return stationInfo;
-  }
-
-  private getStationInfo(): ChargingStationInfo {
-    const defaultStationInfo: Partial<ChargingStationInfo> = {
-      enableStatistics: false,
-      remoteAuthorization: true,
-      currentOutType: CurrentType.AC,
-      mainVoltageMeterValues: true,
-      phaseLineToLineVoltageMeterValues: false,
-      customValueLimitationMeterValues: true,
-      ocppStrictCompliance: true,
-      outOfOrderEndMeterValues: false,
-      beginEndMeterValues: false,
-      meteringPerTransaction: true,
-      transactionDataMeterValues: false,
-      supervisionUrlOcppConfiguration: false,
-      supervisionUrlOcppKey: VendorParametersKey.ConnectionUrl,
-      ocppVersion: OCPPVersion.VERSION_16,
-      ocppPersistentConfiguration: true,
-      stationInfoPersistentConfiguration: true,
-      automaticTransactionGeneratorPersistentConfiguration: true,
-      autoReconnectMaxRetries: -1,
-      registrationMaxRetries: -1,
-      reconnectExponentialDelay: false,
-      stopTransactionsOnStopped: true,
-    };
-    const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate();
-    const stationInfoFromFile: ChargingStationInfo | undefined = this.getStationInfoFromFile(
-      stationInfoFromTemplate?.stationInfoPersistentConfiguration,
-    );
+    return stationInfo
+  }
+
+  private getStationInfo (options?: ChargingStationOptions): ChargingStationInfo {
+    const stationInfoFromTemplate = this.getStationInfoFromTemplate()
+    options?.persistentConfiguration != null &&
+      (stationInfoFromTemplate.stationInfoPersistentConfiguration = options.persistentConfiguration)
+    const stationInfoFromFile = this.getStationInfoFromFile(
+      stationInfoFromTemplate.stationInfoPersistentConfiguration
+    )
+    let stationInfo: ChargingStationInfo
     // Priority:
     // 1. charging station info from template
     // 2. charging station info from configuration file
-    if (stationInfoFromFile?.templateHash === stationInfoFromTemplate.templateHash) {
-      return { ...defaultStationInfo, ...stationInfoFromFile! };
+    if (
+      stationInfoFromFile != null &&
+      stationInfoFromFile.templateHash === stationInfoFromTemplate.templateHash
+    ) {
+      stationInfo = stationInfoFromFile
+    } else {
+      stationInfo = stationInfoFromTemplate
+      stationInfoFromFile != null &&
+        propagateSerialNumber(this.getTemplateFromFile(), stationInfoFromFile, stationInfo)
     }
-    stationInfoFromFile &&
-      propagateSerialNumber(
-        this.getTemplateFromFile()!,
-        stationInfoFromFile,
-        stationInfoFromTemplate,
-      );
-    return { ...defaultStationInfo, ...stationInfoFromTemplate };
+    return setChargingStationOptions(
+      mergeDeepRight(Constants.DEFAULT_STATION_INFO, stationInfo),
+      options
+    )
   }
 
-  private saveStationInfo(): void {
+  private saveStationInfo (): void {
     if (this.stationInfo?.stationInfoPersistentConfiguration === true) {
-      this.saveConfiguration();
+      this.saveConfiguration()
     }
   }
 
-  private handleUnsupportedVersion(version: OCPPVersion | undefined) {
-    const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`;
-    logger.error(`${this.logPrefix()} ${errorMsg}`);
-    throw new BaseError(errorMsg);
+  private handleUnsupportedVersion (version: OCPPVersion | undefined): void {
+    const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`
+    logger.error(`${this.logPrefix()} ${errorMsg}`)
+    throw new BaseError(errorMsg)
   }
 
-  private initialize(): void {
-    const stationTemplate = this.getTemplateFromFile()!;
-    checkTemplate(stationTemplate, this.logPrefix(), this.templateFile);
+  private initialize (options?: ChargingStationOptions): void {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const stationTemplate = this.getTemplateFromFile()!
+    checkTemplate(stationTemplate, this.logPrefix(), this.templateFile)
     this.configurationFile = join(
       dirname(this.templateFile.replace('station-templates', 'configurations')),
-      `${getHashId(this.index, stationTemplate)}.json`,
-    );
-    const stationConfiguration = this.getConfigurationFromFile();
+      `${getHashId(this.index, stationTemplate)}.json`
+    )
+    const stationConfiguration = this.getConfigurationFromFile()
     if (
-      stationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash &&
-      // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-      (stationConfiguration?.connectorsStatus || stationConfiguration?.evsesStatus)
+      stationConfiguration?.stationInfo?.templateHash === stationTemplate.templateHash &&
+      (stationConfiguration?.connectorsStatus != null || stationConfiguration?.evsesStatus != null)
     ) {
-      checkConfiguration(stationConfiguration, this.logPrefix(), this.configurationFile);
-      this.initializeConnectorsOrEvsesFromFile(stationConfiguration);
+      checkConfiguration(stationConfiguration, this.logPrefix(), this.configurationFile)
+      this.initializeConnectorsOrEvsesFromFile(stationConfiguration)
     } else {
-      this.initializeConnectorsOrEvsesFromTemplate(stationTemplate);
+      this.initializeConnectorsOrEvsesFromTemplate(stationTemplate)
     }
-    this.stationInfo = this.getStationInfo();
+    this.stationInfo = this.getStationInfo(options)
     if (
       this.stationInfo.firmwareStatus === FirmwareStatus.Installing &&
-      isNotEmptyString(this.stationInfo.firmwareVersion) &&
-      isNotEmptyString(this.stationInfo.firmwareVersionPattern)
+      isNotEmptyString(this.stationInfo.firmwareVersionPattern) &&
+      isNotEmptyString(this.stationInfo.firmwareVersion)
     ) {
-      const patternGroup: number | undefined =
+      const patternGroup =
         this.stationInfo.firmwareUpgrade?.versionUpgrade?.patternGroup ??
-        this.stationInfo.firmwareVersion?.split('.').length;
-      const match = this.stationInfo
-        .firmwareVersion!.match(new RegExp(this.stationInfo.firmwareVersionPattern!))!
-        .slice(1, patternGroup! + 1);
-      const patchLevelIndex = match.length - 1;
-      match[patchLevelIndex] = (
-        convertToInt(match[patchLevelIndex]) +
-        this.stationInfo.firmwareUpgrade!.versionUpgrade!.step!
-      ).toString();
-      this.stationInfo.firmwareVersion = match?.join('.');
-    }
-    this.saveStationInfo();
-    this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl();
-    if (this.stationInfo?.enableStatistics === true) {
+        this.stationInfo.firmwareVersion.split('.').length
+      const match = new RegExp(this.stationInfo.firmwareVersionPattern)
+        .exec(this.stationInfo.firmwareVersion)
+        ?.slice(1, patternGroup + 1)
+      if (match != null) {
+        const patchLevelIndex = match.length - 1
+        match[patchLevelIndex] = (
+          convertToInt(match[patchLevelIndex]) +
+          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          this.stationInfo.firmwareUpgrade!.versionUpgrade!.step!
+        ).toString()
+        this.stationInfo.firmwareVersion = match.join('.')
+      }
+    }
+    this.saveStationInfo()
+    this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl()
+    if (this.stationInfo.enableStatistics === true) {
       this.performanceStatistics = PerformanceStatistics.getInstance(
         this.stationInfo.hashId,
-        this.stationInfo.chargingStationId!,
-        this.configuredSupervisionUrl,
-      );
+        this.stationInfo.chargingStationId,
+        this.configuredSupervisionUrl
+      )
+    }
+    const bootNotificationRequest = createBootNotificationRequest(this.stationInfo)
+    if (bootNotificationRequest == null) {
+      const errorMsg = 'Error while creating boot notification request'
+      logger.error(`${this.logPrefix()} ${errorMsg}`)
+      throw new BaseError(errorMsg)
     }
-    this.bootNotificationRequest = createBootNotificationRequest(this.stationInfo);
-    this.powerDivider = this.getPowerDivider();
+    this.bootNotificationRequest = bootNotificationRequest
+    this.powerDivider = this.getPowerDivider()
     // OCPP configuration
-    this.ocppConfiguration = this.getOcppConfiguration();
-    this.initializeOcppConfiguration();
-    this.initializeOcppServices();
-    this.once(ChargingStationEvents.accepted, () => {
-      this.startMessageSequence().catch((error) => {
-        logger.error(`${this.logPrefix()} Error while starting the message sequence:`, error);
-      });
-    });
-    if (this.stationInfo?.autoRegister === true) {
+    this.ocppConfiguration = this.getOcppConfiguration(options?.persistentConfiguration)
+    this.initializeOcppConfiguration()
+    this.initializeOcppServices()
+    if (this.stationInfo.autoRegister === true) {
       this.bootNotificationResponse = {
         currentTime: new Date(),
         interval: millisecondsToSeconds(this.getHeartbeatInterval()),
-        status: RegistrationStatusEnumType.ACCEPTED,
-      };
+        status: RegistrationStatusEnumType.ACCEPTED
+      }
     }
   }
 
-  private initializeOcppServices(): void {
-    const ocppVersion = this.stationInfo?.ocppVersion;
+  private initializeOcppServices (): void {
+    const ocppVersion = this.stationInfo?.ocppVersion
     switch (ocppVersion) {
       case OCPPVersion.VERSION_16:
         this.ocppIncomingRequestService =
-          OCPP16IncomingRequestService.getInstance<OCPP16IncomingRequestService>();
+          OCPP16IncomingRequestService.getInstance<OCPP16IncomingRequestService>()
         this.ocppRequestService = OCPP16RequestService.getInstance<OCPP16RequestService>(
-          OCPP16ResponseService.getInstance<OCPP16ResponseService>(),
-        );
-        break;
+          OCPP16ResponseService.getInstance<OCPP16ResponseService>()
+        )
+        break
       case OCPPVersion.VERSION_20:
       case OCPPVersion.VERSION_201:
         this.ocppIncomingRequestService =
-          OCPP20IncomingRequestService.getInstance<OCPP20IncomingRequestService>();
+          OCPP20IncomingRequestService.getInstance<OCPP20IncomingRequestService>()
         this.ocppRequestService = OCPP20RequestService.getInstance<OCPP20RequestService>(
-          OCPP20ResponseService.getInstance<OCPP20ResponseService>(),
-        );
-        break;
+          OCPP20ResponseService.getInstance<OCPP20ResponseService>()
+        )
+        break
       default:
-        this.handleUnsupportedVersion(ocppVersion);
-        break;
+        this.handleUnsupportedVersion(ocppVersion)
+        break
     }
   }
 
-  private initializeOcppConfiguration(): void {
-    if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.HeartbeatInterval))) {
-      addConfigurationKey(this, StandardParametersKey.HeartbeatInterval, '0');
+  private initializeOcppConfiguration (): void {
+    if (getConfigurationKey(this, StandardParametersKey.HeartbeatInterval) == null) {
+      addConfigurationKey(this, StandardParametersKey.HeartbeatInterval, '0')
     }
-    if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.HeartBeatInterval))) {
-      addConfigurationKey(this, StandardParametersKey.HeartBeatInterval, '0', { visible: false });
+    if (getConfigurationKey(this, StandardParametersKey.HeartBeatInterval) == null) {
+      addConfigurationKey(this, StandardParametersKey.HeartBeatInterval, '0', { visible: false })
     }
     if (
       this.stationInfo?.supervisionUrlOcppConfiguration === true &&
-      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
-      isNullOrUndefined(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!))
+      isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) &&
+      getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey) == null
     ) {
       addConfigurationKey(
         this,
-        this.stationInfo.supervisionUrlOcppKey!,
+        this.stationInfo.supervisionUrlOcppKey,
         this.configuredSupervisionUrl.href,
-        { reboot: true },
-      );
+        { reboot: true }
+      )
     } else if (
       this.stationInfo?.supervisionUrlOcppConfiguration === false &&
-      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
-      !isNullOrUndefined(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!))
+      isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) &&
+      getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey) != null
     ) {
-      deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!, { save: false });
+      deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey, { save: false })
     }
     if (
       isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) &&
-      isNullOrUndefined(getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!))
+      getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey) == null
     ) {
       addConfigurationKey(
         this,
-        this.stationInfo.amperageLimitationOcppKey!,
-        (
-          this.stationInfo.maximumAmperage! * getAmperageLimitationUnitDivider(this.stationInfo)
-        ).toString(),
-      );
+        this.stationInfo.amperageLimitationOcppKey,
+        // prettier-ignore
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        (this.stationInfo.maximumAmperage! * getAmperageLimitationUnitDivider(this.stationInfo)).toString()
+      )
     }
-    if (
-      isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles))
-    ) {
+    if (getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles) == null) {
       addConfigurationKey(
         this,
         StandardParametersKey.SupportedFeatureProfiles,
-        `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.FirmwareManagement},${SupportedFeatureProfiles.LocalAuthListManagement},${SupportedFeatureProfiles.SmartCharging},${SupportedFeatureProfiles.RemoteTrigger}`,
-      );
+        `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.FirmwareManagement},${SupportedFeatureProfiles.LocalAuthListManagement},${SupportedFeatureProfiles.SmartCharging},${SupportedFeatureProfiles.RemoteTrigger}`
+      )
     }
     addConfigurationKey(
       this,
       StandardParametersKey.NumberOfConnectors,
       this.getNumberOfConnectors().toString(),
       { readonly: true },
-      { overwrite: true },
-    );
-    if (
-      isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.MeterValuesSampledData))
-    ) {
+      { overwrite: true }
+    )
+    if (getConfigurationKey(this, StandardParametersKey.MeterValuesSampledData) == null) {
       addConfigurationKey(
         this,
         StandardParametersKey.MeterValuesSampledData,
-        MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
-      );
+        MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
+      )
     }
-    if (
-      isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.ConnectorPhaseRotation))
-    ) {
-      const connectorsPhaseRotation: string[] = [];
+    if (getConfigurationKey(this, StandardParametersKey.ConnectorPhaseRotation) == null) {
+      const connectorsPhaseRotation: string[] = []
       if (this.hasEvses) {
         for (const evseStatus of this.evses.values()) {
           for (const connectorId of evseStatus.connectors.keys()) {
             connectorsPhaseRotation.push(
-              getPhaseRotationValue(connectorId, this.getNumberOfPhases())!,
-            );
+              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+              getPhaseRotationValue(connectorId, this.getNumberOfPhases())!
+            )
           }
         }
       } else {
         for (const connectorId of this.connectors.keys()) {
           connectorsPhaseRotation.push(
-            getPhaseRotationValue(connectorId, this.getNumberOfPhases())!,
-          );
+            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+            getPhaseRotationValue(connectorId, this.getNumberOfPhases())!
+          )
         }
       }
       addConfigurationKey(
         this,
         StandardParametersKey.ConnectorPhaseRotation,
-        connectorsPhaseRotation.toString(),
-      );
+        connectorsPhaseRotation.toString()
+      )
     }
-    if (
-      isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests))
-    ) {
-      addConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests, 'true');
+    if (getConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests) == null) {
+      addConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests, 'true')
     }
     if (
-      isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled)) &&
-      getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles)?.value?.includes(
-        SupportedFeatureProfiles.LocalAuthListManagement,
-      )
+      getConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled) == null &&
+      hasFeatureProfile(this, SupportedFeatureProfiles.LocalAuthListManagement) === true
     ) {
-      addConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled, 'false');
+      addConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled, 'false')
     }
-    if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut))) {
+    if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut) == null) {
       addConfigurationKey(
         this,
         StandardParametersKey.ConnectionTimeOut,
-        Constants.DEFAULT_CONNECTION_TIMEOUT.toString(),
-      );
+        Constants.DEFAULT_CONNECTION_TIMEOUT.toString()
+      )
     }
-    this.saveOcppConfiguration();
+    this.saveOcppConfiguration()
   }
 
-  private initializeConnectorsOrEvsesFromFile(configuration: ChargingStationConfiguration): void {
-    if (configuration?.connectorsStatus && !configuration?.evsesStatus) {
+  private initializeConnectorsOrEvsesFromFile (configuration: ChargingStationConfiguration): void {
+    if (configuration.connectorsStatus != null && configuration.evsesStatus == null) {
       for (const [connectorId, connectorStatus] of configuration.connectorsStatus.entries()) {
-        this.connectors.set(connectorId, cloneObject<ConnectorStatus>(connectorStatus));
+        this.connectors.set(connectorId, clone<ConnectorStatus>(connectorStatus))
       }
-    } else if (configuration?.evsesStatus && !configuration?.connectorsStatus) {
+    } else if (configuration.evsesStatus != null && configuration.connectorsStatus == null) {
       for (const [evseId, evseStatusConfiguration] of configuration.evsesStatus.entries()) {
-        const evseStatus = cloneObject<EvseStatusConfiguration>(evseStatusConfiguration);
-        delete evseStatus.connectorsStatus;
+        const evseStatus = clone<EvseStatusConfiguration>(evseStatusConfiguration)
+        delete evseStatus.connectorsStatus
         this.evses.set(evseId, {
           ...(evseStatus as EvseStatus),
           connectors: new Map<number, ConnectorStatus>(
+            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
             evseStatusConfiguration.connectorsStatus!.map((connectorStatus, connectorId) => [
               connectorId,
-              connectorStatus,
-            ]),
-          ),
-        });
+              connectorStatus
+            ])
+          )
+        })
       }
-    } else if (configuration?.evsesStatus && configuration?.connectorsStatus) {
-      const errorMsg = `Connectors and evses defined at the same time in configuration file ${this.configurationFile}`;
-      logger.error(`${this.logPrefix()} ${errorMsg}`);
-      throw new BaseError(errorMsg);
+    } else if (configuration.evsesStatus != null && configuration.connectorsStatus != null) {
+      const errorMsg = `Connectors and evses defined at the same time in configuration file ${this.configurationFile}`
+      logger.error(`${this.logPrefix()} ${errorMsg}`)
+      throw new BaseError(errorMsg)
     } else {
-      const errorMsg = `No connectors or evses defined in configuration file ${this.configurationFile}`;
-      logger.error(`${this.logPrefix()} ${errorMsg}`);
-      throw new BaseError(errorMsg);
+      const errorMsg = `No connectors or evses defined in configuration file ${this.configurationFile}`
+      logger.error(`${this.logPrefix()} ${errorMsg}`)
+      throw new BaseError(errorMsg)
     }
   }
 
-  private initializeConnectorsOrEvsesFromTemplate(stationTemplate: ChargingStationTemplate) {
-    if (stationTemplate?.Connectors && !stationTemplate?.Evses) {
-      this.initializeConnectorsFromTemplate(stationTemplate);
-    } else if (stationTemplate?.Evses && !stationTemplate?.Connectors) {
-      this.initializeEvsesFromTemplate(stationTemplate);
-    } else if (stationTemplate?.Evses && stationTemplate?.Connectors) {
-      const errorMsg = `Connectors and evses defined at the same time in template file ${this.templateFile}`;
-      logger.error(`${this.logPrefix()} ${errorMsg}`);
-      throw new BaseError(errorMsg);
+  private initializeConnectorsOrEvsesFromTemplate (stationTemplate: ChargingStationTemplate): void {
+    if (stationTemplate.Connectors != null && stationTemplate.Evses == null) {
+      this.initializeConnectorsFromTemplate(stationTemplate)
+    } else if (stationTemplate.Evses != null && stationTemplate.Connectors == null) {
+      this.initializeEvsesFromTemplate(stationTemplate)
+    } else if (stationTemplate.Evses != null && stationTemplate.Connectors != null) {
+      const errorMsg = `Connectors and evses defined at the same time in template file ${this.templateFile}`
+      logger.error(`${this.logPrefix()} ${errorMsg}`)
+      throw new BaseError(errorMsg)
     } else {
-      const errorMsg = `No connectors or evses defined in template file ${this.templateFile}`;
-      logger.error(`${this.logPrefix()} ${errorMsg}`);
-      throw new BaseError(errorMsg);
+      const errorMsg = `No connectors or evses defined in template file ${this.templateFile}`
+      logger.error(`${this.logPrefix()} ${errorMsg}`)
+      throw new BaseError(errorMsg)
     }
   }
 
-  private initializeConnectorsFromTemplate(stationTemplate: ChargingStationTemplate): void {
-    if (!stationTemplate?.Connectors && this.connectors.size === 0) {
-      const errorMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined`;
-      logger.error(`${this.logPrefix()} ${errorMsg}`);
-      throw new BaseError(errorMsg);
+  private initializeConnectorsFromTemplate (stationTemplate: ChargingStationTemplate): void {
+    if (stationTemplate.Connectors == null && this.connectors.size === 0) {
+      const errorMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined`
+      logger.error(`${this.logPrefix()} ${errorMsg}`)
+      throw new BaseError(errorMsg)
     }
-    if (!stationTemplate?.Connectors?.[0]) {
+    if (stationTemplate.Connectors?.[0] == null) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
-        } with no connector id 0 configuration`,
-      );
+        } with no connector id 0 configuration`
+      )
     }
-    if (stationTemplate?.Connectors) {
+    if (stationTemplate.Connectors != null) {
       const { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors } =
-        checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile);
+        checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile)
       const connectorsConfigHash = createHash(Constants.DEFAULT_HASH_ALGORITHM)
         .update(
-          `${JSON.stringify(stationTemplate?.Connectors)}${configuredMaxConnectors.toString()}`,
+          `${JSON.stringify(stationTemplate.Connectors)}${configuredMaxConnectors.toString()}`
         )
-        .digest('hex');
+        .digest('hex')
       const connectorsConfigChanged =
-        this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash;
-      if (this.connectors?.size === 0 || connectorsConfigChanged) {
-        connectorsConfigChanged && this.connectors.clear();
-        this.connectorsConfigurationHash = connectorsConfigHash;
+        this.connectors.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash
+      if (this.connectors.size === 0 || connectorsConfigChanged) {
+        connectorsConfigChanged && this.connectors.clear()
+        this.connectorsConfigurationHash = connectorsConfigHash
         if (templateMaxConnectors > 0) {
           for (let connectorId = 0; connectorId <= configuredMaxConnectors; connectorId++) {
             if (
               connectorId === 0 &&
-              (!stationTemplate?.Connectors?.[connectorId] ||
-                this.getUseConnectorId0(stationTemplate) === false)
+              // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+              (stationTemplate.Connectors[connectorId] == null ||
+                !this.getUseConnectorId0(stationTemplate))
             ) {
-              continue;
+              continue
             }
             const templateConnectorId =
-              connectorId > 0 && stationTemplate?.randomConnectors
-                ? getRandomInteger(templateMaxAvailableConnectors, 1)
-                : connectorId;
-            const connectorStatus = stationTemplate?.Connectors[templateConnectorId];
+              connectorId > 0 && stationTemplate.randomConnectors === true
+                ? randomInt(1, templateMaxAvailableConnectors)
+                : connectorId
+            const connectorStatus = stationTemplate.Connectors[templateConnectorId]
             checkStationInfoConnectorStatus(
               templateConnectorId,
               connectorStatus,
               this.logPrefix(),
-              this.templateFile,
-            );
-            this.connectors.set(connectorId, cloneObject<ConnectorStatus>(connectorStatus));
+              this.templateFile
+            )
+            this.connectors.set(connectorId, clone<ConnectorStatus>(connectorStatus))
           }
-          initializeConnectorsMapStatus(this.connectors, this.logPrefix());
-          this.saveConnectorsStatus();
+          initializeConnectorsMapStatus(this.connectors, this.logPrefix())
+          this.saveConnectorsStatus()
         } else {
           logger.warn(
             `${this.logPrefix()} Charging station information from template ${
               this.templateFile
-            } with no connectors configuration defined, cannot create connectors`,
-          );
+            } with no connectors configuration defined, cannot create connectors`
+          )
         }
       }
     } else {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
-        } with no connectors configuration defined, using already defined connectors`,
-      );
+        } with no connectors configuration defined, using already defined connectors`
+      )
     }
   }
 
-  private initializeEvsesFromTemplate(stationTemplate: ChargingStationTemplate): void {
-    if (!stationTemplate?.Evses && this.evses.size === 0) {
-      const errorMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`;
-      logger.error(`${this.logPrefix()} ${errorMsg}`);
-      throw new BaseError(errorMsg);
+  private initializeEvsesFromTemplate (stationTemplate: ChargingStationTemplate): void {
+    if (stationTemplate.Evses == null && this.evses.size === 0) {
+      const errorMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`
+      logger.error(`${this.logPrefix()} ${errorMsg}`)
+      throw new BaseError(errorMsg)
     }
-    if (!stationTemplate?.Evses?.[0]) {
+    if (stationTemplate.Evses?.[0] == null) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
-        } with no evse id 0 configuration`,
-      );
+        } with no evse id 0 configuration`
+      )
     }
-    if (!stationTemplate?.Evses?.[0]?.Connectors?.[0]) {
+    if (stationTemplate.Evses?.[0]?.Connectors[0] == null) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
-        } with evse id 0 with no connector id 0 configuration`,
-      );
+        } with evse id 0 with no connector id 0 configuration`
+      )
     }
-    if (Object.keys(stationTemplate?.Evses?.[0]?.Connectors as object).length > 1) {
+    if (Object.keys(stationTemplate.Evses?.[0]?.Connectors as object).length > 1) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
-        } with evse id 0 with more than one connector configuration, only connector id 0 configuration will be used`,
-      );
+        } with evse id 0 with more than one connector configuration, only connector id 0 configuration will be used`
+      )
     }
-    if (stationTemplate?.Evses) {
+    if (stationTemplate.Evses != null) {
       const evsesConfigHash = createHash(Constants.DEFAULT_HASH_ALGORITHM)
-        .update(JSON.stringify(stationTemplate?.Evses))
-        .digest('hex');
+        .update(JSON.stringify(stationTemplate.Evses))
+        .digest('hex')
       const evsesConfigChanged =
-        this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash;
-      if (this.evses?.size === 0 || evsesConfigChanged) {
-        evsesConfigChanged && this.evses.clear();
-        this.evsesConfigurationHash = evsesConfigHash;
-        const templateMaxEvses = getMaxNumberOfEvses(stationTemplate?.Evses);
+        this.evses.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash
+      if (this.evses.size === 0 || evsesConfigChanged) {
+        evsesConfigChanged && this.evses.clear()
+        this.evsesConfigurationHash = evsesConfigHash
+        const templateMaxEvses = getMaxNumberOfEvses(stationTemplate.Evses)
         if (templateMaxEvses > 0) {
           for (const evseKey in stationTemplate.Evses) {
-            const evseId = convertToInt(evseKey);
+            const evseId = convertToInt(evseKey)
             this.evses.set(evseId, {
               connectors: buildConnectorsMap(
-                stationTemplate?.Evses[evseKey]?.Connectors,
+                stationTemplate.Evses[evseKey].Connectors,
                 this.logPrefix(),
-                this.templateFile,
+                this.templateFile
               ),
-              availability: AvailabilityType.Operative,
-            });
-            initializeConnectorsMapStatus(this.evses.get(evseId)!.connectors, this.logPrefix());
+              availability: AvailabilityType.Operative
+            })
+            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+            initializeConnectorsMapStatus(this.evses.get(evseId)!.connectors, this.logPrefix())
           }
-          this.saveEvsesStatus();
+          this.saveEvsesStatus()
         } else {
           logger.warn(
             `${this.logPrefix()} Charging station information from template ${
               this.templateFile
-            } with no evses configuration defined, cannot create evses`,
-          );
+            } with no evses configuration defined, cannot create evses`
+          )
         }
       }
     } else {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
-        } with no evses configuration defined, using already defined evses`,
-      );
+        } with no evses configuration defined, using already defined evses`
+      )
     }
   }
 
-  private getConfigurationFromFile(): ChargingStationConfiguration | undefined {
-    let configuration: ChargingStationConfiguration | undefined;
+  private getConfigurationFromFile (): ChargingStationConfiguration | undefined {
+    let configuration: ChargingStationConfiguration | undefined
     if (isNotEmptyString(this.configurationFile) && existsSync(this.configurationFile)) {
       try {
         if (this.sharedLRUCache.hasChargingStationConfiguration(this.configurationFileHash)) {
           configuration = this.sharedLRUCache.getChargingStationConfiguration(
-            this.configurationFileHash,
-          );
+            this.configurationFileHash
+          )
         } else {
-          const measureId = `${FileType.ChargingStationConfiguration} read`;
-          const beginId = PerformanceStatistics.beginMeasure(measureId);
+          const measureId = `${FileType.ChargingStationConfiguration} read`
+          const beginId = PerformanceStatistics.beginMeasure(measureId)
           configuration = JSON.parse(
-            readFileSync(this.configurationFile, 'utf8'),
-          ) as ChargingStationConfiguration;
-          PerformanceStatistics.endMeasure(measureId, beginId);
-          this.sharedLRUCache.setChargingStationConfiguration(configuration);
-          this.configurationFileHash = configuration.configurationHash!;
+            readFileSync(this.configurationFile, 'utf8')
+          ) as ChargingStationConfiguration
+          PerformanceStatistics.endMeasure(measureId, beginId)
+          this.sharedLRUCache.setChargingStationConfiguration(configuration)
+          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          this.configurationFileHash = configuration.configurationHash!
         }
       } catch (error) {
         handleFileException(
           this.configurationFile,
           FileType.ChargingStationConfiguration,
           error as NodeJS.ErrnoException,
-          this.logPrefix(),
-        );
+          this.logPrefix()
+        )
       }
     }
-    return configuration;
+    return configuration
   }
 
-  private saveAutomaticTransactionGeneratorConfiguration(): void {
+  private saveAutomaticTransactionGeneratorConfiguration (): void {
     if (this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration === true) {
-      this.saveConfiguration();
+      this.saveConfiguration()
     }
   }
 
-  private saveConnectorsStatus() {
-    this.saveConfiguration();
+  private saveConnectorsStatus (): void {
+    this.saveConfiguration()
   }
 
-  private saveEvsesStatus() {
-    this.saveConfiguration();
+  private saveEvsesStatus (): void {
+    this.saveConfiguration()
   }
 
-  private saveConfiguration(): void {
+  private saveConfiguration (): void {
     if (isNotEmptyString(this.configurationFile)) {
       try {
         if (!existsSync(dirname(this.configurationFile))) {
-          mkdirSync(dirname(this.configurationFile), { recursive: true });
+          mkdirSync(dirname(this.configurationFile), { recursive: true })
         }
-        let configurationData: ChargingStationConfiguration = this.getConfigurationFromFile()
-          ? cloneObject<ChargingStationConfiguration>(this.getConfigurationFromFile()!)
-          : {};
-        if (this.stationInfo?.stationInfoPersistentConfiguration === true && this.stationInfo) {
-          configurationData.stationInfo = this.stationInfo;
+        const configurationFromFile = this.getConfigurationFromFile()
+        let configurationData: ChargingStationConfiguration =
+          configurationFromFile != null
+            ? clone<ChargingStationConfiguration>(configurationFromFile)
+            : {}
+        if (this.stationInfo?.stationInfoPersistentConfiguration === true) {
+          configurationData.stationInfo = this.stationInfo
         } else {
-          delete configurationData.stationInfo;
+          delete configurationData.stationInfo
         }
         if (
           this.stationInfo?.ocppPersistentConfiguration === true &&
           Array.isArray(this.ocppConfiguration?.configurationKey)
         ) {
-          configurationData.configurationKey = this.ocppConfiguration?.configurationKey;
+          configurationData.configurationKey = this.ocppConfiguration.configurationKey
         } else {
-          delete configurationData.configurationKey;
+          delete configurationData.configurationKey
         }
-        configurationData = merge<ChargingStationConfiguration>(
+        configurationData = mergeDeepRight(
           configurationData,
-          buildChargingStationAutomaticTransactionGeneratorConfiguration(this),
-        );
-        if (
-          !this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration ||
-          !this.getAutomaticTransactionGeneratorConfiguration()
-        ) {
-          delete configurationData.automaticTransactionGenerator;
+          buildChargingStationAutomaticTransactionGeneratorConfiguration(this)
+        )
+        if (this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration !== true) {
+          delete configurationData.automaticTransactionGenerator
         }
         if (this.connectors.size > 0) {
-          configurationData.connectorsStatus = buildConnectorsStatus(this);
+          configurationData.connectorsStatus = buildConnectorsStatus(this)
         } else {
-          delete configurationData.connectorsStatus;
+          delete configurationData.connectorsStatus
         }
         if (this.evses.size > 0) {
-          configurationData.evsesStatus = buildEvsesStatus(this);
+          configurationData.evsesStatus = buildEvsesStatus(this)
         } else {
-          delete configurationData.evsesStatus;
+          delete configurationData.evsesStatus
         }
-        delete configurationData.configurationHash;
+        delete configurationData.configurationHash
         const configurationHash = createHash(Constants.DEFAULT_HASH_ALGORITHM)
           .update(
             JSON.stringify({
@@ -1664,259 +1734,290 @@ export class ChargingStation extends EventEmitter {
               configurationKey: configurationData.configurationKey,
               automaticTransactionGenerator: configurationData.automaticTransactionGenerator,
               ...(this.connectors.size > 0 && {
-                connectorsStatus: configurationData.connectorsStatus,
+                connectorsStatus: configurationData.connectorsStatus
               }),
-              ...(this.evses.size > 0 && { evsesStatus: configurationData.evsesStatus }),
-            } as ChargingStationConfiguration),
+              ...(this.evses.size > 0 && { evsesStatus: configurationData.evsesStatus })
+            } satisfies ChargingStationConfiguration)
           )
-          .digest('hex');
+          .digest('hex')
         if (this.configurationFileHash !== configurationHash) {
           AsyncLock.runExclusive(AsyncLockType.configuration, () => {
-            configurationData.configurationHash = configurationHash;
-            const measureId = `${FileType.ChargingStationConfiguration} write`;
-            const beginId = PerformanceStatistics.beginMeasure(measureId);
+            configurationData.configurationHash = configurationHash
+            const measureId = `${FileType.ChargingStationConfiguration} write`
+            const beginId = PerformanceStatistics.beginMeasure(measureId)
             writeFileSync(
               this.configurationFile,
               JSON.stringify(configurationData, undefined, 2),
-              'utf8',
-            );
-            PerformanceStatistics.endMeasure(measureId, beginId);
-            this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash);
-            this.sharedLRUCache.setChargingStationConfiguration(configurationData);
-            this.configurationFileHash = configurationHash;
-          }).catch((error) => {
+              'utf8'
+            )
+            PerformanceStatistics.endMeasure(measureId, beginId)
+            this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash)
+            this.sharedLRUCache.setChargingStationConfiguration(configurationData)
+            this.configurationFileHash = configurationHash
+          }).catch((error: unknown) => {
             handleFileException(
               this.configurationFile,
               FileType.ChargingStationConfiguration,
               error as NodeJS.ErrnoException,
-              this.logPrefix(),
-            );
-          });
+              this.logPrefix()
+            )
+          })
         } else {
           logger.debug(
             `${this.logPrefix()} Not saving unchanged charging station configuration file ${
               this.configurationFile
-            }`,
-          );
+            }`
+          )
         }
       } catch (error) {
         handleFileException(
           this.configurationFile,
           FileType.ChargingStationConfiguration,
           error as NodeJS.ErrnoException,
-          this.logPrefix(),
-        );
+          this.logPrefix()
+        )
       }
     } else {
       logger.error(
-        `${this.logPrefix()} Trying to save charging station configuration to undefined configuration file`,
-      );
+        `${this.logPrefix()} Trying to save charging station configuration to undefined configuration file`
+      )
     }
   }
 
-  private getOcppConfigurationFromTemplate(): ChargingStationOcppConfiguration | undefined {
-    return this.getTemplateFromFile()?.Configuration;
+  private getOcppConfigurationFromTemplate (): ChargingStationOcppConfiguration | undefined {
+    return this.getTemplateFromFile()?.Configuration
   }
 
-  private getOcppConfigurationFromFile(): ChargingStationOcppConfiguration | undefined {
-    const configurationKey = this.getConfigurationFromFile()?.configurationKey;
-    if (this.stationInfo?.ocppPersistentConfiguration === true && Array.isArray(configurationKey)) {
-      return { configurationKey };
+  private getOcppConfigurationFromFile (
+    ocppPersistentConfiguration?: boolean
+  ): ChargingStationOcppConfiguration | undefined {
+    const configurationKey = this.getConfigurationFromFile()?.configurationKey
+    if (ocppPersistentConfiguration === true && Array.isArray(configurationKey)) {
+      return { configurationKey }
     }
-    return undefined;
+    return undefined
   }
 
-  private getOcppConfiguration(): ChargingStationOcppConfiguration | undefined {
+  private getOcppConfiguration (
+    ocppPersistentConfiguration: boolean | undefined = this.stationInfo?.ocppPersistentConfiguration
+  ): ChargingStationOcppConfiguration | undefined {
     let ocppConfiguration: ChargingStationOcppConfiguration | undefined =
-      this.getOcppConfigurationFromFile();
-    if (!ocppConfiguration) {
-      ocppConfiguration = this.getOcppConfigurationFromTemplate();
+      this.getOcppConfigurationFromFile(ocppPersistentConfiguration)
+    if (ocppConfiguration == null) {
+      ocppConfiguration = this.getOcppConfigurationFromTemplate()
     }
-    return ocppConfiguration;
+    return ocppConfiguration
   }
 
-  private async onOpen(): Promise<void> {
-    if (this.isWebSocketConnectionOpened() === true) {
+  private async onOpen (): Promise<void> {
+    if (this.isWebSocketConnectionOpened()) {
+      this.emit(ChargingStationEvents.updated)
       logger.info(
-        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} succeeded`,
-      );
-      let registrationRetryCount = 0;
-      if (this.isRegistered() === false) {
+        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.href} succeeded`
+      )
+      let registrationRetryCount = 0
+      if (!this.isRegistered()) {
         // Send BootNotification
         do {
           this.bootNotificationResponse = await this.ocppRequestService.requestHandler<
-            BootNotificationRequest,
-            BootNotificationResponse
+          BootNotificationRequest,
+          BootNotificationResponse
           >(this, RequestCommand.BOOT_NOTIFICATION, this.bootNotificationRequest, {
-            skipBufferingOnError: true,
-          });
-          if (this.isRegistered() === false) {
-            this.stationInfo?.registrationMaxRetries !== -1 && ++registrationRetryCount;
+            skipBufferingOnError: true
+          })
+          // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+          if (this.bootNotificationResponse?.currentTime != null) {
+            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+            this.bootNotificationResponse.currentTime = convertToDate(
+              this.bootNotificationResponse.currentTime
+            )!
+          }
+          if (!this.isRegistered()) {
+            this.stationInfo?.registrationMaxRetries !== -1 && ++registrationRetryCount
             await sleep(
-              this?.bootNotificationResponse?.interval
+              // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+              this.bootNotificationResponse?.interval != null
                 ? secondsToMilliseconds(this.bootNotificationResponse.interval)
-                : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL,
-            );
+                : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL
+            )
           }
         } while (
-          this.isRegistered() === false &&
-          (registrationRetryCount <= this.stationInfo.registrationMaxRetries! ||
+          !this.isRegistered() &&
+          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+          (registrationRetryCount <= this.stationInfo!.registrationMaxRetries! ||
             this.stationInfo?.registrationMaxRetries === -1)
-        );
+        )
       }
-      if (this.isRegistered() === true) {
-        this.emit(ChargingStationEvents.registered);
-        if (this.inAcceptedState() === true) {
-          this.emit(ChargingStationEvents.accepted);
+      if (this.isRegistered()) {
+        this.emit(ChargingStationEvents.registered)
+        if (this.inAcceptedState()) {
+          this.emit(ChargingStationEvents.accepted)
         }
       } else {
+        if (this.inRejectedState()) {
+          this.emit(ChargingStationEvents.rejected)
+        }
         logger.error(
-          `${this.logPrefix()} Registration failure: maximum retries reached (${registrationRetryCount}) or retry disabled (${this
-            .stationInfo?.registrationMaxRetries})`,
-        );
+          `${this.logPrefix()} Registration failure: maximum retries reached (${registrationRetryCount}) or retry disabled (${
+            this.stationInfo?.registrationMaxRetries
+          })`
+        )
       }
-      this.autoReconnectRetryCount = 0;
-      this.emit(ChargingStationEvents.updated);
+      this.wsConnectionRetryCount = 0
+      this.emit(ChargingStationEvents.updated)
     } else {
       logger.warn(
-        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} failed`,
-      );
+        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.href} failed`
+      )
     }
   }
 
-  private async onClose(code: WebSocketCloseEventStatusCode, reason: Buffer): Promise<void> {
+  private onClose (code: WebSocketCloseEventStatusCode, reason: Buffer): void {
+    this.emit(ChargingStationEvents.disconnected)
+    this.emit(ChargingStationEvents.updated)
     switch (code) {
       // Normal close
       case WebSocketCloseEventStatusCode.CLOSE_NORMAL:
       case WebSocketCloseEventStatusCode.CLOSE_NO_STATUS:
         logger.info(
           `${this.logPrefix()} WebSocket normally closed with status '${getWebSocketCloseEventStatusString(
-            code,
-          )}' and reason '${reason.toString()}'`,
-        );
-        this.autoReconnectRetryCount = 0;
-        break;
+            code
+          )}' and reason '${reason.toString()}'`
+        )
+        this.wsConnectionRetryCount = 0
+        break
       // Abnormal close
       default:
         logger.error(
           `${this.logPrefix()} WebSocket abnormally closed with status '${getWebSocketCloseEventStatusString(
-            code,
-          )}' and reason '${reason.toString()}'`,
-        );
-        this.started === true && (await this.reconnect());
-        break;
+            code
+          )}' and reason '${reason.toString()}'`
+        )
+        this.started &&
+          this.reconnect()
+            .then(() => {
+              this.emit(ChargingStationEvents.updated)
+            })
+            .catch((error: unknown) =>
+              logger.error(`${this.logPrefix()} Error while reconnecting:`, error)
+            )
+        break
     }
-    this.emit(ChargingStationEvents.updated);
   }
 
-  private getCachedRequest(messageType: MessageType, messageId: string): CachedRequest | undefined {
-    const cachedRequest = this.requests.get(messageId);
-    if (Array.isArray(cachedRequest) === true) {
-      return cachedRequest;
+  private getCachedRequest (
+    messageType: MessageType | undefined,
+    messageId: string
+  ): CachedRequest | undefined {
+    const cachedRequest = this.requests.get(messageId)
+    if (Array.isArray(cachedRequest)) {
+      return cachedRequest
     }
     throw new OCPPError(
       ErrorType.PROTOCOL_ERROR,
-      `Cached request for message id ${messageId} ${OCPPServiceUtils.getMessageTypeString(
-        messageType,
+      `Cached request for message id ${messageId} ${getMessageTypeString(
+        messageType
       )} is not an array`,
       undefined,
-      cachedRequest as JsonType,
-    );
+      cachedRequest
+    )
   }
 
-  private async handleIncomingMessage(request: IncomingRequest): Promise<void> {
-    const [messageType, messageId, commandName, commandPayload] = request;
+  private async handleIncomingMessage (request: IncomingRequest): Promise<void> {
+    const [messageType, messageId, commandName, commandPayload] = request
     if (this.stationInfo?.enableStatistics === true) {
-      this.performanceStatistics?.addRequestStatistic(commandName, messageType);
+      this.performanceStatistics?.addRequestStatistic(commandName, messageType)
     }
     logger.debug(
       `${this.logPrefix()} << Command '${commandName}' received request payload: ${JSON.stringify(
-        request,
-      )}`,
-    );
+        request
+      )}`
+    )
     // Process the message
     await this.ocppIncomingRequestService.incomingRequestHandler(
       this,
       messageId,
       commandName,
-      commandPayload,
-    );
-    this.emit(ChargingStationEvents.updated);
+      commandPayload
+    )
+    this.emit(ChargingStationEvents.updated)
   }
 
-  private handleResponseMessage(response: Response): void {
-    const [messageType, messageId, commandPayload] = response;
-    if (this.requests.has(messageId) === false) {
+  private handleResponseMessage (response: Response): void {
+    const [messageType, messageId, commandPayload] = response
+    if (!this.requests.has(messageId)) {
       // Error
       throw new OCPPError(
         ErrorType.INTERNAL_ERROR,
         `Response for unknown message id ${messageId}`,
         undefined,
-        commandPayload,
-      );
+        commandPayload
+      )
     }
     // Respond
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     const [responseCallback, , requestCommandName, requestPayload] = this.getCachedRequest(
       messageType,
-      messageId,
-    )!;
+      messageId
+    )!
     logger.debug(
-      `${this.logPrefix()} << Command '${
-        requestCommandName ?? Constants.UNKNOWN_COMMAND
-      }' received response payload: ${JSON.stringify(response)}`,
-    );
-    responseCallback(commandPayload, requestPayload);
+      `${this.logPrefix()} << Command '${requestCommandName}' received response payload: ${JSON.stringify(
+        response
+      )}`
+    )
+    responseCallback(commandPayload, requestPayload)
   }
 
-  private handleErrorMessage(errorResponse: ErrorResponse): void {
-    const [messageType, messageId, errorType, errorMessage, errorDetails] = errorResponse;
-    if (this.requests.has(messageId) === false) {
+  private handleErrorMessage (errorResponse: ErrorResponse): void {
+    const [messageType, messageId, errorType, errorMessage, errorDetails] = errorResponse
+    if (!this.requests.has(messageId)) {
       // Error
       throw new OCPPError(
         ErrorType.INTERNAL_ERROR,
         `Error response for unknown message id ${messageId}`,
         undefined,
-        { errorType, errorMessage, errorDetails },
-      );
+        { errorType, errorMessage, errorDetails }
+      )
     }
-    const [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)!;
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)!
     logger.debug(
-      `${this.logPrefix()} << Command '${
-        requestCommandName ?? Constants.UNKNOWN_COMMAND
-      }' received error response payload: ${JSON.stringify(errorResponse)}`,
-    );
-    errorCallback(new OCPPError(errorType, errorMessage, requestCommandName, errorDetails));
+      `${this.logPrefix()} << Command '${requestCommandName}' received error response payload: ${JSON.stringify(
+        errorResponse
+      )}`
+    )
+    errorCallback(new OCPPError(errorType, errorMessage, requestCommandName, errorDetails))
   }
 
-  private async onMessage(data: RawData): Promise<void> {
-    let request: IncomingRequest | Response | ErrorResponse | undefined;
-    let messageType: MessageType | undefined;
-    let errorMsg: string;
+  private async onMessage (data: RawData): Promise<void> {
+    let request: IncomingRequest | Response | ErrorResponse | undefined
+    let messageType: MessageType | undefined
+    let errorMsg: string
     try {
       // eslint-disable-next-line @typescript-eslint/no-base-to-string
-      request = JSON.parse(data.toString()) as IncomingRequest | Response | ErrorResponse;
-      if (Array.isArray(request) === true) {
-        [messageType] = request;
+      request = JSON.parse(data.toString()) as IncomingRequest | Response | ErrorResponse
+      if (Array.isArray(request)) {
+        [messageType] = request
         // Check the type of message
         switch (messageType) {
           // Incoming Message
           case MessageType.CALL_MESSAGE:
-            await this.handleIncomingMessage(request as IncomingRequest);
-            break;
+            await this.handleIncomingMessage(request as IncomingRequest)
+            break
           // Response Message
           case MessageType.CALL_RESULT_MESSAGE:
-            this.handleResponseMessage(request as Response);
-            break;
+            this.handleResponseMessage(request as Response)
+            break
           // Error Message
           case MessageType.CALL_ERROR_MESSAGE:
-            this.handleErrorMessage(request as ErrorResponse);
-            break;
+            this.handleErrorMessage(request as ErrorResponse)
+            break
           // Unknown Message
           default:
             // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-            errorMsg = `Wrong message type ${messageType}`;
-            logger.error(`${this.logPrefix()} ${errorMsg}`);
-            throw new OCPPError(ErrorType.PROTOCOL_ERROR, errorMsg);
+            errorMsg = `Wrong message type ${messageType}`
+            logger.error(`${this.logPrefix()} ${errorMsg}`)
+            throw new OCPPError(ErrorType.PROTOCOL_ERROR, errorMsg)
         }
       } else {
         throw new OCPPError(
@@ -1924,414 +2025,414 @@ export class ChargingStation extends EventEmitter {
           'Incoming message is not an array',
           undefined,
           {
-            request,
-          },
-        );
+            request
+          }
+        )
       }
     } catch (error) {
-      let commandName: IncomingRequestCommand | undefined;
-      let requestCommandName: RequestCommand | IncomingRequestCommand | undefined;
-      let errorCallback: ErrorCallback;
-      const [, messageId] = request!;
+      if (!Array.isArray(request)) {
+        logger.error(`${this.logPrefix()} Incoming message '${request}' parsing error:`, error)
+        return
+      }
+      let commandName: IncomingRequestCommand | undefined
+      let requestCommandName: RequestCommand | IncomingRequestCommand | undefined
+      let errorCallback: ErrorCallback
+      const [, messageId] = request
       switch (messageType) {
         case MessageType.CALL_MESSAGE:
-          [, , commandName] = request as IncomingRequest;
+          [, , commandName] = request as IncomingRequest
           // Send error
-          await this.ocppRequestService.sendError(this, messageId, error as OCPPError, commandName);
-          break;
+          await this.ocppRequestService.sendError(this, messageId, error as OCPPError, commandName)
+          break
         case MessageType.CALL_RESULT_MESSAGE:
         case MessageType.CALL_ERROR_MESSAGE:
-          if (this.requests.has(messageId) === true) {
-            [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)!;
+          if (this.requests.has(messageId)) {
+            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+            [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)!
             // Reject the deferred promise in case of error at response handling (rejecting an already fulfilled promise is a no-op)
-            errorCallback(error as OCPPError, false);
+            errorCallback(error as OCPPError, false)
           } else {
             // Remove the request from the cache in case of error at response handling
-            this.requests.delete(messageId);
+            this.requests.delete(messageId)
           }
-          break;
+          break
       }
-      if (error instanceof OCPPError === false) {
+      if (!(error instanceof OCPPError)) {
         logger.warn(
           `${this.logPrefix()} Error thrown at incoming OCPP command '${
-            commandName ?? requestCommandName ?? Constants.UNKNOWN_COMMAND
+            commandName ?? requestCommandName ?? Constants.UNKNOWN_OCPP_COMMAND
             // eslint-disable-next-line @typescript-eslint/no-base-to-string
           }' message '${data.toString()}' handling is not an OCPPError:`,
-          error,
-        );
+          error
+        )
       }
       logger.error(
         `${this.logPrefix()} Incoming OCPP command '${
-          commandName ?? requestCommandName ?? Constants.UNKNOWN_COMMAND
+          commandName ?? requestCommandName ?? Constants.UNKNOWN_OCPP_COMMAND
           // eslint-disable-next-line @typescript-eslint/no-base-to-string
         }' message '${data.toString()}'${
-          messageType !== MessageType.CALL_MESSAGE
-            ? ` matching cached request '${JSON.stringify(this.requests.get(messageId))}'`
+          this.requests.has(messageId)
+            ? ` matching cached request '${JSON.stringify(this.getCachedRequest(messageType, messageId))}'`
             : ''
         } processing error:`,
-        error,
-      );
+        error
+      )
     }
   }
 
-  private onPing(): void {
-    logger.debug(`${this.logPrefix()} Received a WS ping (rfc6455) from the server`);
+  private onPing (): void {
+    logger.debug(`${this.logPrefix()} Received a WS ping (rfc6455) from the server`)
   }
 
-  private onPong(): void {
-    logger.debug(`${this.logPrefix()} Received a WS pong (rfc6455) from the server`);
+  private onPong (): void {
+    logger.debug(`${this.logPrefix()} Received a WS pong (rfc6455) from the server`)
   }
 
-  private onError(error: WSError): void {
-    this.closeWSConnection();
-    logger.error(`${this.logPrefix()} WebSocket error:`, error);
+  private onError (error: WSError): void {
+    this.closeWSConnection()
+    logger.error(`${this.logPrefix()} WebSocket error:`, error)
   }
 
-  private getEnergyActiveImportRegister(connectorStatus: ConnectorStatus, rounded = false): number {
+  private getEnergyActiveImportRegister (
+    connectorStatus: ConnectorStatus | undefined,
+    rounded = false
+  ): number {
     if (this.stationInfo?.meteringPerTransaction === true) {
       return (
-        (rounded === true
-          ? Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue!)
+        (rounded
+          ? connectorStatus?.transactionEnergyActiveImportRegisterValue != null
+            ? Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue)
+            : undefined
           : connectorStatus?.transactionEnergyActiveImportRegisterValue) ?? 0
-      );
+      )
     }
     return (
-      (rounded === true
-        ? Math.round(connectorStatus.energyActiveImportRegisterValue!)
+      (rounded
+        ? connectorStatus?.energyActiveImportRegisterValue != null
+          ? Math.round(connectorStatus.energyActiveImportRegisterValue)
+          : undefined
         : connectorStatus?.energyActiveImportRegisterValue) ?? 0
-    );
+    )
   }
 
-  private getUseConnectorId0(stationTemplate?: ChargingStationTemplate): boolean {
-    return stationTemplate?.useConnectorId0 ?? true;
+  private getUseConnectorId0 (stationTemplate?: ChargingStationTemplate): boolean {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    return stationTemplate?.useConnectorId0 ?? Constants.DEFAULT_STATION_INFO.useConnectorId0!
   }
 
-  private async stopRunningTransactions(reason?: StopTransactionReason): Promise<void> {
+  private async stopRunningTransactions (reason?: StopTransactionReason): Promise<void> {
     if (this.hasEvses) {
       for (const [evseId, evseStatus] of this.evses) {
         if (evseId === 0) {
-          continue;
+          continue
         }
         for (const [connectorId, connectorStatus] of evseStatus.connectors) {
           if (connectorStatus.transactionStarted === true) {
-            await this.stopTransactionOnConnector(connectorId, reason);
+            await this.stopTransactionOnConnector(connectorId, reason)
           }
         }
       }
     } else {
       for (const connectorId of this.connectors.keys()) {
         if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) {
-          await this.stopTransactionOnConnector(connectorId, reason);
+          await this.stopTransactionOnConnector(connectorId, reason)
         }
       }
     }
   }
 
   // 0 for disabling
-  private getConnectionTimeout(): number {
-    if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut) !== undefined) {
+  private getConnectionTimeout (): number {
+    if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut) != null) {
       return convertToInt(
-        getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)!.value! ??
-          Constants.DEFAULT_CONNECTION_TIMEOUT,
-      );
+        getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)?.value ??
+          Constants.DEFAULT_CONNECTION_TIMEOUT
+      )
     }
-    return Constants.DEFAULT_CONNECTION_TIMEOUT;
+    return Constants.DEFAULT_CONNECTION_TIMEOUT
   }
 
-  private getPowerDivider(): number {
-    let powerDivider = this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors();
+  private getPowerDivider (): number {
+    let powerDivider = this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()
     if (this.stationInfo?.powerSharedByConnectors === true) {
-      powerDivider = this.getNumberOfRunningTransactions();
+      powerDivider = this.getNumberOfRunningTransactions()
     }
-    return powerDivider;
+    return powerDivider
   }
 
-  private getMaximumAmperage(stationInfo?: ChargingStationInfo): number | undefined {
-    const maximumPower = (stationInfo ?? this.stationInfo).maximumPower!;
+  private getMaximumAmperage (stationInfo?: ChargingStationInfo): number | undefined {
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const maximumPower = (stationInfo ?? this.stationInfo!).maximumPower!
     switch (this.getCurrentOutType(stationInfo)) {
       case CurrentType.AC:
         return ACElectricUtils.amperagePerPhaseFromPower(
           this.getNumberOfPhases(stationInfo),
           maximumPower / (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()),
-          this.getVoltageOut(stationInfo),
-        );
+          this.getVoltageOut(stationInfo)
+        )
       case CurrentType.DC:
-        return DCElectricUtils.amperage(maximumPower, this.getVoltageOut(stationInfo));
+        return DCElectricUtils.amperage(maximumPower, this.getVoltageOut(stationInfo))
     }
   }
 
-  private getCurrentOutType(stationInfo?: ChargingStationInfo): CurrentType {
-    return (stationInfo ?? this.stationInfo).currentOutType ?? CurrentType.AC;
+  private getCurrentOutType (stationInfo?: ChargingStationInfo): CurrentType {
+    return (
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      (stationInfo ?? this.stationInfo!).currentOutType ??
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      Constants.DEFAULT_STATION_INFO.currentOutType!
+    )
   }
 
-  private getVoltageOut(stationInfo?: ChargingStationInfo): Voltage {
+  private getVoltageOut (stationInfo?: ChargingStationInfo): Voltage {
     return (
-      (stationInfo ?? this.stationInfo).voltageOut ??
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      (stationInfo ?? this.stationInfo!).voltageOut ??
       getDefaultVoltageOut(this.getCurrentOutType(stationInfo), this.logPrefix(), this.templateFile)
-    );
+    )
   }
 
-  private getAmperageLimitation(): number | undefined {
+  private getAmperageLimitation (): number | undefined {
     if (
       isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) &&
-      getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) !== undefined
+      getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey) != null
     ) {
       return (
-        convertToInt(
-          getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!)?.value,
-        ) / getAmperageLimitationUnitDivider(this.stationInfo)
-      );
+        convertToInt(getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey)?.value) /
+        getAmperageLimitationUnitDivider(this.stationInfo)
+      )
     }
   }
 
-  private async startMessageSequence(): Promise<void> {
+  private async startMessageSequence (ATGStopAbsoluteDuration?: boolean): Promise<void> {
     if (this.stationInfo?.autoRegister === true) {
       await this.ocppRequestService.requestHandler<
-        BootNotificationRequest,
-        BootNotificationResponse
+      BootNotificationRequest,
+      BootNotificationResponse
       >(this, RequestCommand.BOOT_NOTIFICATION, this.bootNotificationRequest, {
-        skipBufferingOnError: true,
-      });
+        skipBufferingOnError: true
+      })
     }
     // Start WebSocket ping
-    this.startWebSocketPing();
+    this.startWebSocketPing()
     // Start heartbeat
-    this.startHeartbeat();
+    this.startHeartbeat()
     // Initialize connectors status
     if (this.hasEvses) {
       for (const [evseId, evseStatus] of this.evses) {
         if (evseId > 0) {
           for (const [connectorId, connectorStatus] of evseStatus.connectors) {
-            const connectorBootStatus = getBootConnectorStatus(this, connectorId, connectorStatus);
-            await OCPPServiceUtils.sendAndSetConnectorStatus(
+            await sendAndSetConnectorStatus(
               this,
               connectorId,
-              connectorBootStatus,
-              evseId,
-            );
+              getBootConnectorStatus(this, connectorId, connectorStatus),
+              evseId
+            )
           }
         }
       }
     } else {
       for (const connectorId of this.connectors.keys()) {
         if (connectorId > 0) {
-          const connectorBootStatus = getBootConnectorStatus(
+          await sendAndSetConnectorStatus(
             this,
             connectorId,
-            this.getConnectorStatus(connectorId)!,
-          );
-          await OCPPServiceUtils.sendAndSetConnectorStatus(this, connectorId, connectorBootStatus);
+            getBootConnectorStatus(
+              this,
+              connectorId,
+              // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+              this.getConnectorStatus(connectorId)!
+            )
+          )
         }
       }
     }
-    if (this.stationInfo.firmwareStatus === FirmwareStatus.Installing) {
+    if (this.stationInfo?.firmwareStatus === FirmwareStatus.Installing) {
       await this.ocppRequestService.requestHandler<
-        FirmwareStatusNotificationRequest,
-        FirmwareStatusNotificationResponse
+      FirmwareStatusNotificationRequest,
+      FirmwareStatusNotificationResponse
       >(this, RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
-        status: FirmwareStatus.Installed,
-      });
-      this.stationInfo.firmwareStatus = FirmwareStatus.Installed;
+        status: FirmwareStatus.Installed
+      })
+      this.stationInfo.firmwareStatus = FirmwareStatus.Installed
     }
 
     // Start the ATG
-    if (this.getAutomaticTransactionGeneratorConfiguration().enable === true) {
-      this.startAutomaticTransactionGenerator();
+    if (this.getAutomaticTransactionGeneratorConfiguration()?.enable === true) {
+      this.startAutomaticTransactionGenerator(undefined, ATGStopAbsoluteDuration)
     }
-    this.flushMessageBuffer();
+    this.flushMessageBuffer()
   }
 
-  private async stopMessageSequence(
-    reason?: StopTransactionReason,
-    stopTransactions = this.stationInfo?.stopTransactionsOnStopped,
-  ): Promise<void> {
+  private internalStopMessageSequence (): void {
     // Stop WebSocket ping
-    this.stopWebSocketPing();
+    this.stopWebSocketPing()
     // Stop heartbeat
-    this.stopHeartbeat();
+    this.stopHeartbeat()
     // Stop the ATG
     if (this.automaticTransactionGenerator?.started === true) {
-      this.stopAutomaticTransactionGenerator();
+      this.stopAutomaticTransactionGenerator()
     }
+  }
+
+  private async stopMessageSequence (
+    reason?: StopTransactionReason,
+    stopTransactions?: boolean
+  ): Promise<void> {
+    this.internalStopMessageSequence()
     // Stop ongoing transactions
-    stopTransactions && (await this.stopRunningTransactions(reason));
+    stopTransactions === true && (await this.stopRunningTransactions(reason))
     if (this.hasEvses) {
       for (const [evseId, evseStatus] of this.evses) {
         if (evseId > 0) {
           for (const [connectorId, connectorStatus] of evseStatus.connectors) {
-            await this.ocppRequestService.requestHandler<
-              StatusNotificationRequest,
-              StatusNotificationResponse
-            >(
+            await sendAndSetConnectorStatus(
               this,
-              RequestCommand.STATUS_NOTIFICATION,
-              OCPPServiceUtils.buildStatusNotificationRequest(
-                this,
-                connectorId,
-                ConnectorStatusEnum.Unavailable,
-                evseId,
-              ),
-            );
-            delete connectorStatus?.status;
+              connectorId,
+              ConnectorStatusEnum.Unavailable,
+              evseId
+            )
+            delete connectorStatus.status
           }
         }
       }
     } else {
       for (const connectorId of this.connectors.keys()) {
         if (connectorId > 0) {
-          await this.ocppRequestService.requestHandler<
-            StatusNotificationRequest,
-            StatusNotificationResponse
-          >(
-            this,
-            RequestCommand.STATUS_NOTIFICATION,
-            OCPPServiceUtils.buildStatusNotificationRequest(
-              this,
-              connectorId,
-              ConnectorStatusEnum.Unavailable,
-            ),
-          );
-          delete this.getConnectorStatus(connectorId)?.status;
+          await sendAndSetConnectorStatus(this, connectorId, ConnectorStatusEnum.Unavailable)
+          delete this.getConnectorStatus(connectorId)?.status
         }
       }
     }
   }
 
-  private startWebSocketPing(): void {
-    const webSocketPingInterval: number =
-      getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval) !== undefined
+  private startWebSocketPing (): void {
+    const webSocketPingInterval =
+      getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval) != null
         ? convertToInt(
-            getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value,
-          )
-        : 0;
-    if (webSocketPingInterval > 0 && this.webSocketPingSetInterval === undefined) {
-      this.webSocketPingSetInterval = setInterval(() => {
-        if (this.isWebSocketConnectionOpened() === true) {
-          this.wsConnection?.ping();
+          getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value
+        )
+        : 0
+    if (webSocketPingInterval > 0 && this.wsPingSetInterval == null) {
+      this.wsPingSetInterval = setInterval(() => {
+        if (this.isWebSocketConnectionOpened()) {
+          this.wsConnection?.ping()
         }
-      }, secondsToMilliseconds(webSocketPingInterval));
+      }, secondsToMilliseconds(webSocketPingInterval))
       logger.info(
         `${this.logPrefix()} WebSocket ping started every ${formatDurationSeconds(
-          webSocketPingInterval,
-        )}`,
-      );
-    } else if (this.webSocketPingSetInterval !== undefined) {
+          webSocketPingInterval
+        )}`
+      )
+    } else if (this.wsPingSetInterval != null) {
       logger.info(
         `${this.logPrefix()} WebSocket ping already started every ${formatDurationSeconds(
-          webSocketPingInterval,
-        )}`,
-      );
+          webSocketPingInterval
+        )}`
+      )
     } else {
       logger.error(
-        `${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval}, not starting the WebSocket ping`,
-      );
+        `${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval}, not starting the WebSocket ping`
+      )
     }
   }
 
-  private stopWebSocketPing(): void {
-    if (this.webSocketPingSetInterval !== undefined) {
-      clearInterval(this.webSocketPingSetInterval);
-      delete this.webSocketPingSetInterval;
+  private stopWebSocketPing (): void {
+    if (this.wsPingSetInterval != null) {
+      clearInterval(this.wsPingSetInterval)
+      delete this.wsPingSetInterval
     }
   }
 
-  private getConfiguredSupervisionUrl(): URL {
-    let configuredSupervisionUrl: string;
-    const supervisionUrls = this.stationInfo?.supervisionUrls ?? Configuration.getSupervisionUrls();
+  private getConfiguredSupervisionUrl (): URL {
+    let configuredSupervisionUrl: string
+    const supervisionUrls = this.stationInfo?.supervisionUrls ?? Configuration.getSupervisionUrls()
     if (isNotEmptyArray(supervisionUrls)) {
-      let configuredSupervisionUrlIndex: number;
+      let configuredSupervisionUrlIndex: number
       switch (Configuration.getSupervisionUrlDistribution()) {
         case SupervisionUrlDistribution.RANDOM:
-          configuredSupervisionUrlIndex = Math.floor(
-            secureRandom() * (supervisionUrls as string[]).length,
-          );
-          break;
+          configuredSupervisionUrlIndex = Math.floor(secureRandom() * supervisionUrls.length)
+          break
         case SupervisionUrlDistribution.ROUND_ROBIN:
         case SupervisionUrlDistribution.CHARGING_STATION_AFFINITY:
         default:
-          Object.values(SupervisionUrlDistribution).includes(
-            Configuration.getSupervisionUrlDistribution()!,
-          ) === false &&
-            logger.error(
+          !Object.values(SupervisionUrlDistribution).includes(
+            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+            Configuration.getSupervisionUrlDistribution()!
+          ) &&
+            logger.warn(
               // eslint-disable-next-line @typescript-eslint/no-base-to-string
-              `${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' from values '${SupervisionUrlDistribution.toString()}', defaulting to ${
+              `${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' in configuration from values '${SupervisionUrlDistribution.toString()}', defaulting to '${
                 SupervisionUrlDistribution.CHARGING_STATION_AFFINITY
-              }`,
-            );
-          configuredSupervisionUrlIndex = (this.index - 1) % (supervisionUrls as string[]).length;
-          break;
+              }'`
+            )
+          configuredSupervisionUrlIndex = (this.index - 1) % supervisionUrls.length
+          break
       }
-      configuredSupervisionUrl = (supervisionUrls as string[])[configuredSupervisionUrlIndex];
+      configuredSupervisionUrl = supervisionUrls[configuredSupervisionUrlIndex]
     } else {
-      configuredSupervisionUrl = supervisionUrls as string;
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      configuredSupervisionUrl = supervisionUrls!
     }
     if (isNotEmptyString(configuredSupervisionUrl)) {
-      return new URL(configuredSupervisionUrl);
+      return new URL(configuredSupervisionUrl)
     }
-    const errorMsg = 'No supervision url(s) configured';
-    logger.error(`${this.logPrefix()} ${errorMsg}`);
-    throw new BaseError(`${errorMsg}`);
+    const errorMsg = 'No supervision url(s) configured'
+    logger.error(`${this.logPrefix()} ${errorMsg}`)
+    throw new BaseError(errorMsg)
   }
 
-  private stopHeartbeat(): void {
-    if (this.heartbeatSetInterval !== undefined) {
-      clearInterval(this.heartbeatSetInterval);
-      delete this.heartbeatSetInterval;
+  private stopHeartbeat (): void {
+    if (this.heartbeatSetInterval != null) {
+      clearInterval(this.heartbeatSetInterval)
+      delete this.heartbeatSetInterval
     }
   }
 
-  private terminateWSConnection(): void {
-    if (this.isWebSocketConnectionOpened() === true) {
-      this.wsConnection?.terminate();
-      this.wsConnection = null;
+  private terminateWSConnection (): void {
+    if (this.isWebSocketConnectionOpened()) {
+      this.wsConnection?.terminate()
+      this.wsConnection = null
     }
   }
 
-  private async reconnect(): Promise<void> {
-    // Stop WebSocket ping
-    this.stopWebSocketPing();
-    // Stop heartbeat
-    this.stopHeartbeat();
-    // Stop the ATG if needed
-    if (this.getAutomaticTransactionGeneratorConfiguration().stopOnConnectionFailure === true) {
-      this.stopAutomaticTransactionGenerator();
-    }
+  private async reconnect (): Promise<void> {
     if (
-      this.autoReconnectRetryCount < this.stationInfo.autoReconnectMaxRetries! ||
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      this.wsConnectionRetryCount < this.stationInfo!.autoReconnectMaxRetries! ||
       this.stationInfo?.autoReconnectMaxRetries === -1
     ) {
-      ++this.autoReconnectRetryCount;
+      this.wsConnectionRetried = true
+      ++this.wsConnectionRetryCount
       const reconnectDelay =
         this.stationInfo?.reconnectExponentialDelay === true
-          ? exponentialDelay(this.autoReconnectRetryCount)
-          : secondsToMilliseconds(this.getConnectionTimeout());
-      const reconnectDelayWithdraw = 1000;
+          ? exponentialDelay(this.wsConnectionRetryCount)
+          : secondsToMilliseconds(this.getConnectionTimeout())
+      const reconnectDelayWithdraw = 1000
       const reconnectTimeout =
-        reconnectDelay && reconnectDelay - reconnectDelayWithdraw > 0
-          ? reconnectDelay - reconnectDelayWithdraw
-          : 0;
+        reconnectDelay - reconnectDelayWithdraw > 0 ? reconnectDelay - reconnectDelayWithdraw : 0
       logger.error(
         `${this.logPrefix()} WebSocket connection retry in ${roundTo(
           reconnectDelay,
-          2,
-        )}ms, timeout ${reconnectTimeout}ms`,
-      );
-      await sleep(reconnectDelay);
+          2
+        )}ms, timeout ${reconnectTimeout}ms`
+      )
+      await sleep(reconnectDelay)
       logger.error(
-        `${this.logPrefix()} WebSocket connection retry #${this.autoReconnectRetryCount.toString()}`,
-      );
+        `${this.logPrefix()} WebSocket connection retry #${this.wsConnectionRetryCount.toString()}`
+      )
       this.openWSConnection(
         {
-          handshakeTimeout: reconnectTimeout,
+          handshakeTimeout: reconnectTimeout
         },
-        { closeOpened: true },
-      );
+        { closeOpened: true }
+      )
     } else if (this.stationInfo?.autoReconnectMaxRetries !== -1) {
       logger.error(
         `${this.logPrefix()} WebSocket connection retries failure: maximum retries reached (${
-          this.autoReconnectRetryCount
-        }) or retries disabled (${this.stationInfo?.autoReconnectMaxRetries})`,
-      );
+          this.wsConnectionRetryCount
+        }) or retries disabled (${this.stationInfo?.autoReconnectMaxRetries})`
+      )
     }
   }
 }