build(deps-dev): apply updates
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index 281ce2471df76192932f6dc0691efc655ddf6300..926915fb016a5497fd519667dff1199052b666e2 100644 (file)
@@ -1,67 +1,16 @@
-// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
+// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
 
-import { createHash } from 'node:crypto'
+import { createHash, randomInt } from 'node:crypto'
 import { EventEmitter } from 'node:events'
-import { type FSWatcher, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
+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 { mergeDeepRight, once } from 'rambda'
 import { type RawData, WebSocket } from 'ws'
 
-import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator.js'
-import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel.js'
-import {
-  addConfigurationKey,
-  deleteConfigurationKey,
-  getConfigurationKey,
-  setConfigurationKeyValue
-} from './ConfigurationKeyUtils.js'
-import {
-  buildConnectorsMap,
-  checkChargingStation,
-  checkConfiguration,
-  checkConnectorsConfiguration,
-  checkStationInfoConnectorStatus,
-  checkTemplate,
-  createBootNotificationRequest,
-  createSerialNumber,
-  getAmperageLimitationUnitDivider,
-  getBootConnectorStatus,
-  getChargingStationConnectorChargingProfilesPowerLimit,
-  getChargingStationId,
-  getDefaultVoltageOut,
-  getHashId,
-  getIdTagsFile,
-  getMaxNumberOfEvses,
-  getNumberOfReservableConnectors,
-  getPhaseRotationValue,
-  hasFeatureProfile,
-  hasReservationExpired,
-  initializeConnectorsMapStatus,
-  propagateSerialNumber,
-  stationTemplateToStationInfo,
-  warnTemplateKeysDeprecation
-} from './Helpers.js'
-import { IdTagsCache } from './IdTagsCache.js'
-import {
-  OCPP16IncomingRequestService,
-  OCPP16RequestService,
-  OCPP16ResponseService,
-  OCPP20IncomingRequestService,
-  OCPP20RequestService,
-  OCPP20ResponseService,
-  type OCPPIncomingRequestService,
-  type OCPPRequestService,
-  buildMeterValue,
-  buildStatusNotificationRequest,
-  buildTransactionEndMeterValue,
-  getMessageTypeString,
-  sendAndSetConnectorStatus
-} from './ocpp/index.js'
-import { SharedLRUCache } from './SharedLRUCache.js'
 import { BaseError, OCPPError } from '../exception/index.js'
 import { PerformanceStatistics } from '../performance/index.js'
 import {
@@ -74,6 +23,7 @@ import {
   ChargingStationEvents,
   type ChargingStationInfo,
   type ChargingStationOcppConfiguration,
+  type ChargingStationOptions,
   type ChargingStationTemplate,
   type ConnectorStatus,
   ConnectorStatusEnum,
@@ -87,7 +37,6 @@ import {
   FirmwareStatus,
   type FirmwareStatusNotificationRequest,
   type FirmwareStatusNotificationResponse,
-  type FirmwareUpgrade,
   type HeartbeatRequest,
   type HeartbeatResponse,
   type IncomingRequest,
@@ -107,71 +56,122 @@ import {
   type Response,
   StandardParametersKey,
   type Status,
-  type StatusNotificationRequest,
-  type StatusNotificationResponse,
   type StopTransactionReason,
   type StopTransactionRequest,
   type StopTransactionResponse,
   SupervisionUrlDistribution,
   SupportedFeatureProfiles,
   type Voltage,
-  type WSError,
   WebSocketCloseEventStatusCode,
+  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,
-  logPrefix,
   logger,
+  logPrefix,
   min,
-  once,
   roundTo,
   secureRandom,
   sleep,
   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 stationInfo?: ChargingStationInfo
   public started: boolean
   public starting: boolean
   public idTagsCache: IdTagsCache
-  public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined
-  public ocppConfiguration!: ChargingStationOcppConfiguration | undefined
+  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 | undefined
+  public performanceStatistics?: PerformanceStatistics
   public heartbeatSetInterval?: NodeJS.Timeout
   public ocppRequestService!: OCPPRequestService
-  public bootNotificationRequest!: BootNotificationRequest
-  public bootNotificationResponse!: BootNotificationResponse | undefined
-  public powerDivider!: number
+  public bootNotificationRequest?: BootNotificationRequest
+  public bootNotificationResponse?: BootNotificationResponse
+  public powerDivider?: number
   private stopping: boolean
   private configurationFile!: string
   private configurationFileHash!: string
@@ -181,21 +181,23 @@ export class ChargingStation extends EventEmitter {
   private ocppIncomingRequestService!: OCPPIncomingRequestService
   private readonly messageBuffer: Set<string>
   private configuredSupervisionUrl!: URL
-  private autoReconnectRetryCount: number
-  private templateFileWatcher!: FSWatcher | undefined
+  private wsConnectionRetried: boolean
+  private wsConnectionRetryCount: number
+  private templateFileWatcher?: FSWatcher
   private templateFileHash!: string
   private readonly sharedLRUCache: SharedLRUCache
-  private webSocketPingSetInterval?: NodeJS.Timeout
+  private wsPingSetInterval?: NodeJS.Timeout
   private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel
   private flushMessageBufferSetInterval?: NodeJS.Timeout
 
-  constructor (index: number, templateFile: string) {
+  constructor (index: number, templateFile: string, options?: ChargingStationOptions) {
     super()
     this.started = false
     this.starting = false
     this.stopping = false
     this.wsConnection = null
-    this.autoReconnectRetryCount = 0
+    this.wsConnectionRetried = false
+    this.wsConnectionRetryCount = 0
     this.index = index
     this.templateFile = templateFile
     this.connectors = new Map<number, ConnectorStatus>()
@@ -206,6 +208,12 @@ export class ChargingStation extends EventEmitter {
     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))
     })
@@ -215,31 +223,62 @@ export class ChargingStation extends EventEmitter {
     this.on(ChargingStationEvents.updated, () => {
       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()
+    this.initialize(options)
+
+    this.add()
+
+    if (this.stationInfo?.autoStart === true) {
+      this.start()
+    }
   }
 
   public get hasEvses (): boolean {
     return this.connectors.size === 0 && this.evses.size > 0
   }
 
-  private get wsConnectionUrl (): URL {
+  public get wsConnectionUrl (): URL {
     return new URL(
       `${
         this.stationInfo?.supervisionUrlOcppConfiguration === true &&
-        isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)?.value)
-          ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-            getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)!.value
+        isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) &&
+        isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey)?.value)
+          ? getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey)?.value
           : this.configuredSupervisionUrl.href
-      }/${this.stationInfo.chargingStationId}`
+      }/${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
     try {
@@ -247,18 +286,19 @@ export class ChargingStation extends EventEmitter {
         readFileSync(this.templateFile, 'utf8')
       ) as ChargingStationTemplate
     } catch {
-      stationTemplate = undefined
+      // Ignore
     }
     return logPrefix(` ${getChargingStationId(this.index, stationTemplate)} |`)
   }
 
   public hasIdTags (): boolean {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    return isNotEmptyArray(this.idTagsCache.getIdTags(getIdTagsFile(this.stationInfo)!))
+    return isNotEmptyArray(this.idTagsCache.getIdTags(getIdTagsFile(this.stationInfo!)!))
   }
 
   public getNumberOfPhases (stationInfo?: ChargingStationInfo): number {
-    const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo
+    // 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
@@ -268,23 +308,23 @@ export class ChargingStation extends EventEmitter {
   }
 
   public isWebSocketConnectionOpened (): boolean {
-    return this?.wsConnection?.readyState === WebSocket.OPEN
+    return this.wsConnection?.readyState === WebSocket.OPEN
   }
 
   public inUnknownState (): boolean {
-    return this?.bootNotificationResponse?.status == null
+    return this.bootNotificationResponse?.status == null
   }
 
   public inPendingState (): boolean {
-    return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.PENDING
+    return this.bootNotificationResponse?.status === RegistrationStatusEnumType.PENDING
   }
 
   public inAcceptedState (): boolean {
-    return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.ACCEPTED
+    return this.bootNotificationResponse?.status === RegistrationStatusEnumType.ACCEPTED
   }
 
   public inRejectedState (): boolean {
-    return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.REJECTED
+    return this.bootNotificationResponse?.status === RegistrationStatusEnumType.REJECTED
   }
 
   public isRegistered (): boolean {
@@ -345,10 +385,11 @@ export class ChargingStation extends EventEmitter {
 
   public getConnectorMaximumAvailablePower (connectorId: number): number {
     let connectorAmperageLimitationPowerLimit: number | undefined
+    const amperageLimitation = this.getAmperageLimitation()
     if (
-      this.getAmperageLimitation() != null &&
+      amperageLimitation != null &&
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      this.getAmperageLimitation()! < this.stationInfo.maximumAmperage!
+      amperageLimitation < this.stationInfo!.maximumAmperage!
     ) {
       connectorAmperageLimitationPowerLimit =
         (this.stationInfo?.currentOutType === CurrentType.AC
@@ -356,16 +397,16 @@ export class ChargingStation extends EventEmitter {
             this.getNumberOfPhases(),
             // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
             this.stationInfo.voltageOut!,
-            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-            this.getAmperageLimitation()! *
+            amperageLimitation *
                 (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors())
           )
           : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-          DCElectricUtils.power(this.stationInfo.voltageOut!, this.getAmperageLimitation()!)) /
-        this.powerDivider
+          DCElectricUtils.power(this.stationInfo!.voltageOut!, amperageLimitation)) /
+        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+        this.powerDivider!
     }
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    const connectorMaximumPower = this.stationInfo.maximumPower! / this.powerDivider
+    const connectorMaximumPower = this.stationInfo!.maximumPower! / this.powerDivider!
     const connectorChargingProfilesPowerLimit =
       getChargingStationConnectorChargingProfilesPowerLimit(this, connectorId)
     return min(
@@ -421,8 +462,10 @@ export class ChargingStation extends EventEmitter {
     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) {
@@ -440,19 +483,18 @@ export class ChargingStation extends EventEmitter {
   }
 
   public getEnergyActiveImportRegisterByTransactionId (
-    transactionId: number,
+    transactionId: number | undefined,
     rounded = false
   ): number {
     return this.getEnergyActiveImportRegister(
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)!)!,
+      this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)!),
       rounded
     )
   }
 
   public getEnergyActiveImportRegisterByConnectorId (connectorId: number, rounded = false): number {
-    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId)!, rounded)
+    return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId), rounded)
   }
 
   public getAuthorizeRemoteTxRequests (): boolean {
@@ -494,14 +536,14 @@ export class ChargingStation extends EventEmitter {
   public setSupervisionUrl (url: string): void {
     if (
       this.stationInfo?.supervisionUrlOcppConfiguration === true &&
-      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey)
+      isNotEmptyString(this.stationInfo.supervisionUrlOcppKey)
     ) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey!, url)
+      setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey, url)
     } else {
-      this.stationInfo.supervisionUrls = url
-      this.saveStationInfo()
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      this.stationInfo!.supervisionUrls = url
       this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl()
+      this.saveStationInfo()
     }
   }
 
@@ -510,7 +552,7 @@ export class ChargingStation extends EventEmitter {
       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
@@ -554,21 +596,22 @@ export class ChargingStation extends EventEmitter {
       logger.error(`${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId}`)
       return
     }
-    if (this.getConnectorStatus(connectorId) == null) {
+    const connectorStatus = this.getConnectorStatus(connectorId)
+    if (connectorStatus == null) {
       logger.error(
         `${this.logPrefix()} Trying to start MeterValues on non existing connector id
           ${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
     } else if (
-      this.getConnectorStatus(connectorId)?.transactionStarted === true &&
-      this.getConnectorStatus(connectorId)?.transactionId == null
+      connectorStatus.transactionStarted === true &&
+      connectorStatus.transactionId == null
     ) {
       logger.error(
         `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id`
@@ -576,13 +619,12 @@ export class ChargingStation extends EventEmitter {
       return
     }
     if (interval > 0) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      this.getConnectorStatus(connectorId)!.transactionSetInterval = setInterval(() => {
+      connectorStatus.transactionSetInterval = setInterval(() => {
         const meterValue = buildMeterValue(
           this,
           connectorId,
           // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-          this.getConnectorStatus(connectorId)!.transactionId!,
+          connectorStatus.transactionId!,
           interval
         )
         this.ocppRequestService
@@ -591,11 +633,11 @@ export class ChargingStation extends EventEmitter {
           RequestCommand.METER_VALUES,
           {
             connectorId,
-            transactionId: this.getConnectorStatus(connectorId)?.transactionId,
+            transactionId: connectorStatus.transactionId,
             meterValue: [meterValue]
           }
         )
-          .catch((error) => {
+          .catch((error: unknown) => {
             logger.error(
               `${this.logPrefix()} Error while sending '${RequestCommand.METER_VALUES}':`,
               error
@@ -612,9 +654,32 @@ export class ChargingStation extends EventEmitter {
   }
 
   public stopMeterValues (connectorId: number): void {
-    if (this.getConnectorStatus(connectorId)?.transactionSetInterval != null) {
-      clearInterval(this.getConnectorStatus(connectorId)?.transactionSetInterval)
+    const connectorStatus = this.getConnectorStatus(connectorId)
+    if (connectorStatus?.transactionSetInterval != null) {
+      clearInterval(connectorStatus.transactionSetInterval)
+    }
+  }
+
+  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 {
@@ -640,15 +705,21 @@ export class ChargingStation extends EventEmitter {
                   } 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()
-                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-                this.idTagsCache.deleteIdTags(getIdTagsFile(this.stationInfo)!)
                 // Restart the ATG
-                this.stopAutomaticTransactionGenerator()
+                const ATGStarted = this.automaticTransactionGenerator?.started
+                if (ATGStarted === true) {
+                  this.stopAutomaticTransactionGenerator()
+                }
                 delete this.automaticTransactionGeneratorConfiguration
-                if (this.getAutomaticTransactionGeneratorConfiguration().enable) {
-                  this.startAutomaticTransactionGenerator()
+                if (
+                  this.getAutomaticTransactionGeneratorConfiguration()?.enable === true &&
+                  ATGStarted === true
+                ) {
+                  this.startAutomaticTransactionGenerator(undefined, true)
                 }
                 if (this.stationInfo?.enableStatistics === true) {
                   this.performanceStatistics?.restart()
@@ -676,7 +747,10 @@ export class ChargingStation extends EventEmitter {
     }
   }
 
-  public async stop (reason?: StopTransactionReason, stopTransactions?: boolean): Promise<void> {
+  public async stop (
+    reason?: StopTransactionReason,
+    stopTransactions = this.stationInfo?.stopTransactionsOnStopped
+  ): Promise<void> {
     if (this.started) {
       if (!this.stopping) {
         this.stopping = true
@@ -685,12 +759,11 @@ export class ChargingStation extends EventEmitter {
         if (this.stationInfo?.enableStatistics === true) {
           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.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash)
         this.emit(ChargingStationEvents.stopped)
         this.stopping = false
       } else {
@@ -704,7 +777,7 @@ export class ChargingStation extends EventEmitter {
   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!)
+    await sleep(this.stationInfo!.resetTime!)
     this.initialize()
     this.start()
   }
@@ -733,26 +806,24 @@ export class ChargingStation extends EventEmitter {
     if (!checkChargingStation(this, this.logPrefix())) {
       return
     }
-    if (this.stationInfo.supervisionUser != null && this.stationInfo.supervisionPassword != null) {
+    if (this.stationInfo?.supervisionUser != null && this.stationInfo.supervisionPassword != null) {
       options.auth = `${this.stationInfo.supervisionUser}:${this.stationInfo.supervisionPassword}`
     }
-    if (params?.closeOpened === true) {
+    if (params.closeOpened === true) {
       this.closeWSConnection()
     }
-    if (params?.terminateOpened === true) {
+    if (params.terminateOpened === true) {
       this.terminateWSConnection()
     }
 
     if (this.isWebSocketConnectionOpened()) {
       logger.warn(
-        `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} is already opened`
+        `${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,
@@ -761,26 +832,23 @@ export class ChargingStation extends EventEmitter {
     )
 
     // 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 {
@@ -790,7 +858,9 @@ export class ChargingStation extends EventEmitter {
     }
   }
 
-  public getAutomaticTransactionGeneratorConfiguration (): AutomaticTransactionGeneratorConfiguration {
+  public getAutomaticTransactionGeneratorConfiguration ():
+  | AutomaticTransactionGeneratorConfiguration
+  | undefined {
     if (this.automaticTransactionGeneratorConfiguration == null) {
       let automaticTransactionGeneratorConfiguration:
       | AutomaticTransactionGeneratorConfiguration
@@ -803,7 +873,7 @@ export class ChargingStation extends EventEmitter {
         stationConfiguration?.automaticTransactionGenerator != null
       ) {
         automaticTransactionGeneratorConfiguration =
-          stationConfiguration?.automaticTransactionGenerator
+          stationConfiguration.automaticTransactionGenerator
       } else {
         automaticTransactionGeneratorConfiguration = stationTemplate?.AutomaticTransactionGenerator
       }
@@ -819,15 +889,17 @@ export class ChargingStation extends EventEmitter {
     return this.getConfigurationFromFile()?.automaticTransactionGeneratorStatuses
   }
 
-  public startAutomaticTransactionGenerator (connectorIds?: number[]): void {
+  public startAutomaticTransactionGenerator (
+    connectorIds?: number[],
+    stopAbsoluteDuration?: boolean
+  ): void {
     this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this)
     if (isNotEmptyArray(connectorIds)) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      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)
@@ -835,8 +907,7 @@ export class ChargingStation extends EventEmitter {
 
   public stopAutomaticTransactionGenerator (connectorIds?: number[]): void {
     if (isNotEmptyArray(connectorIds)) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      for (const connectorId of connectorIds!) {
+      for (const connectorId of connectorIds) {
         this.automaticTransactionGenerator?.stopConnector(connectorId)
       }
     } else {
@@ -853,14 +924,13 @@ export class ChargingStation extends EventEmitter {
     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
     ) {
       const transactionEndMeterValue = buildTransactionEndMeterValue(
         this,
         connectorId,
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        this.getEnergyActiveImportRegisterByTransactionId(transactionId!)
+        this.getEnergyActiveImportRegisterByTransactionId(transactionId)
       )
       await this.ocppRequestService.requestHandler<MeterValuesRequest, MeterValuesResponse>(
         this,
@@ -877,8 +947,7 @@ export class ChargingStation extends EventEmitter {
     StopTransactionResponse
     >(this, RequestCommand.STOP_TRANSACTION, {
       transactionId,
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId!, true),
+      meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId, true),
       ...(reason != null && { reason })
     })
   }
@@ -942,14 +1011,14 @@ export class ChargingStation extends EventEmitter {
     if (this.hasEvses) {
       for (const evseStatus of this.evses.values()) {
         for (const connectorStatus of evseStatus.connectors.values()) {
-          if (connectorStatus?.reservation?.[filterKey] === value) {
+          if (connectorStatus.reservation?.[filterKey] === value) {
             return connectorStatus.reservation
           }
         }
       }
     } else {
       for (const connectorStatus of this.connectors.values()) {
-        if (connectorStatus?.reservation?.[filterKey] === value) {
+        if (connectorStatus.reservation?.[filterKey] === value) {
           return connectorStatus.reservation
         }
       }
@@ -962,15 +1031,14 @@ export class ChargingStation extends EventEmitter {
     connectorId?: number
   ): boolean {
     const reservation = this.getReservationBy('reservationId', reservationId)
-    const reservationExists = reservation !== undefined && !hasReservationExpired(reservation)
+    const reservationExists = reservation != null && !hasReservationExpired(reservation)
     if (arguments.length === 1) {
       return !reservationExists
     } else if (arguments.length > 1) {
-      const userReservation =
-        idTag !== undefined ? this.getReservationBy('idTag', idTag) : undefined
+      const userReservation = idTag != null ? this.getReservationBy('idTag', idTag) : undefined
       const userReservationExists =
-        userReservation !== undefined && !hasReservationExpired(userReservation)
-      const notConnectorZero = connectorId === undefined ? true : connectorId > 0
+        userReservation != null && !hasReservationExpired(userReservation)
+      const notConnectorZero = connectorId == null ? true : connectorId > 0
       const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0
       return (
         !reservationExists && !userReservationExists && notConnectorZero && freeConnectorsAvailable
@@ -1084,40 +1152,38 @@ export class ChargingStation extends EventEmitter {
 
   private getStationInfoFromTemplate (): ChargingStationInfo {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    const stationTemplate: ChargingStationTemplate = this.getTemplateFromFile()!
+    const stationTemplate = this.getTemplateFromFile()!
     checkTemplate(stationTemplate, this.logPrefix(), this.templateFile)
-    const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation, this)
+    const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation)
     warnTemplateKeysDeprecationOnce(stationTemplate, this.logPrefix(), this.templateFile)
-    if (stationTemplate?.Connectors != null) {
+    if (stationTemplate.Connectors != null) {
       checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile)
     }
-    const stationInfo: ChargingStationInfo = stationTemplateToStationInfo(stationTemplate)
+    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)
-    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[]
+    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]
     } else {
-      stationTemplate.power = stationTemplate?.power as number
       stationInfo.maximumPower =
-        stationTemplate?.powerUnit === PowerUnits.KILO_WATT
-          ? stationTemplate.power * 1000
+        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
     if (
+      isNotEmptyString(stationInfo.firmwareVersionPattern) &&
       isNotEmptyString(stationInfo.firmwareVersion) &&
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      !new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion!)
+      !new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion)
     ) {
       logger.warn(
         `${this.logPrefix()} Firmware version '${stationInfo.firmwareVersion}' in template file ${
@@ -1125,56 +1191,60 @@ export class ChargingStation extends EventEmitter {
         } does not match firmware version pattern '${stationInfo.firmwareVersionPattern}'`
       )
     }
-    stationInfo.firmwareUpgrade = merge<FirmwareUpgrade>(
-      {
-        versionUpgrade: {
-          step: 1
-        },
-        reset: true
-      },
-      stationTemplate?.firmwareUpgrade ?? {}
-    )
-    stationInfo.resetTime =
-      stationTemplate?.resetTime != null
-        ? secondsToMilliseconds(stationTemplate.resetTime)
-        : Constants.CHARGING_STATION_DEFAULT_RESET_TIME
+    if (stationTemplate.resetTime != null) {
+      stationInfo.resetTime = secondsToMilliseconds(stationTemplate.resetTime)
+    }
     return stationInfo
   }
 
   private getStationInfoFromFile (
-    stationInfoPersistentConfiguration = true
+    stationInfoPersistentConfiguration: boolean | undefined = Constants.DEFAULT_STATION_INFO
+      .stationInfoPersistentConfiguration
   ): ChargingStationInfo | undefined {
     let stationInfo: ChargingStationInfo | undefined
-    if (stationInfoPersistentConfiguration) {
+    if (stationInfoPersistentConfiguration === true) {
       stationInfo = this.getConfigurationFromFile()?.stationInfo
       if (stationInfo != null) {
-        delete stationInfo?.infoHash
+        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 = Constants.DEFAULT_STATION_INFO
-    const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate()
-    const stationInfoFromFile: ChargingStationInfo | undefined = this.getStationInfoFromFile(
-      stationInfoFromTemplate?.stationInfoPersistentConfiguration
+  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) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      return { ...defaultStationInfo, ...stationInfoFromFile! }
+    if (
+      stationInfoFromFile != null &&
+      stationInfoFromFile.templateHash === stationInfoFromTemplate.templateHash
+    ) {
+      stationInfo = stationInfoFromFile
+    } else {
+      stationInfo = stationInfoFromTemplate
+      stationInfoFromFile != null &&
+        propagateSerialNumber(this.getTemplateFromFile(), stationInfoFromFile, stationInfo)
     }
-    stationInfoFromFile != null &&
-      propagateSerialNumber(
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        this.getTemplateFromFile()!,
-        stationInfoFromFile,
-        stationInfoFromTemplate
-      )
-    return { ...defaultStationInfo, ...stationInfoFromTemplate }
+    return setChargingStationOptions(
+      mergeDeepRight(Constants.DEFAULT_STATION_INFO, stationInfo),
+      options
+    )
   }
 
   private saveStationInfo (): void {
@@ -1189,7 +1259,7 @@ export class ChargingStation extends EventEmitter {
     throw new BaseError(errorMsg)
   }
 
-  private initialize (): void {
+  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)
@@ -1199,8 +1269,7 @@ export class ChargingStation extends EventEmitter {
     )
     const stationConfiguration = this.getConfigurationFromFile()
     if (
-      stationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash &&
-      // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+      stationConfiguration?.stationInfo?.templateHash === stationTemplate.templateHash &&
       (stationConfiguration?.connectorsStatus != null || stationConfiguration?.evsesStatus != null)
     ) {
       checkConfiguration(stationConfiguration, this.logPrefix(), this.configurationFile)
@@ -1208,21 +1277,18 @@ export class ChargingStation extends EventEmitter {
     } else {
       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
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      const match = new RegExp(this.stationInfo.firmwareVersionPattern!)
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        .exec(this.stationInfo.firmwareVersion!)
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        ?.slice(1, patternGroup! + 1)
+        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] = (
@@ -1235,26 +1301,26 @@ export class ChargingStation extends EventEmitter {
     }
     this.saveStationInfo()
     this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl()
-    if (this.stationInfo?.enableStatistics === true) {
+    if (this.stationInfo.enableStatistics === true) {
       this.performanceStatistics = PerformanceStatistics.getInstance(
         this.stationInfo.hashId,
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        this.stationInfo.chargingStationId!,
+        this.stationInfo.chargingStationId,
         this.configuredSupervisionUrl
       )
     }
-    this.bootNotificationRequest = createBootNotificationRequest(this.stationInfo)
+    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 = bootNotificationRequest
     this.powerDivider = this.getPowerDivider()
     // OCPP configuration
-    this.ocppConfiguration = this.getOcppConfiguration()
+    this.ocppConfiguration = this.getOcppConfiguration(options?.persistentConfiguration)
     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) {
+    if (this.stationInfo.autoRegister === true) {
       this.bootNotificationResponse = {
         currentTime: new Date(),
         interval: millisecondsToSeconds(this.getHeartbeatInterval()),
@@ -1296,35 +1362,29 @@ export class ChargingStation extends EventEmitter {
     }
     if (
       this.stationInfo?.supervisionUrlOcppConfiguration === true &&
-      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!) == null
+      isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) &&
+      getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey) == null
     ) {
       addConfigurationKey(
         this,
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        this.stationInfo.supervisionUrlOcppKey!,
+        this.stationInfo.supervisionUrlOcppKey,
         this.configuredSupervisionUrl.href,
         { reboot: true }
       )
     } else if (
       this.stationInfo?.supervisionUrlOcppConfiguration === false &&
-      isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) &&
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!) != null
+      isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) &&
+      getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey) != null
     ) {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!, { save: false })
+      deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey, { save: false })
     }
     if (
       isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) &&
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) == null
+      getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey) == null
     ) {
       addConfigurationKey(
         this,
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        this.stationInfo.amperageLimitationOcppKey!,
+        this.stationInfo.amperageLimitationOcppKey,
         // prettier-ignore
         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
         (this.stationInfo.maximumAmperage! * getAmperageLimitationUnitDivider(this.stationInfo)).toString()
@@ -1396,13 +1456,13 @@ export class ChargingStation extends EventEmitter {
   }
 
   private initializeConnectorsOrEvsesFromFile (configuration: ChargingStationConfiguration): void {
-    if (configuration?.connectorsStatus != null && configuration?.evsesStatus == null) {
+    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 != null && configuration?.connectorsStatus == null) {
+    } else if (configuration.evsesStatus != null && configuration.connectorsStatus == null) {
       for (const [evseId, evseStatusConfiguration] of configuration.evsesStatus.entries()) {
-        const evseStatus = cloneObject<EvseStatusConfiguration>(evseStatusConfiguration)
+        const evseStatus = clone<EvseStatusConfiguration>(evseStatusConfiguration)
         delete evseStatus.connectorsStatus
         this.evses.set(evseId, {
           ...(evseStatus as EvseStatus),
@@ -1415,7 +1475,7 @@ export class ChargingStation extends EventEmitter {
           )
         })
       }
-    } else if (configuration?.evsesStatus != null && configuration?.connectorsStatus != null) {
+    } 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)
@@ -1427,11 +1487,11 @@ export class ChargingStation extends EventEmitter {
   }
 
   private initializeConnectorsOrEvsesFromTemplate (stationTemplate: ChargingStationTemplate): void {
-    if (stationTemplate?.Connectors != null && stationTemplate?.Evses == null) {
+    if (stationTemplate.Connectors != null && stationTemplate.Evses == null) {
       this.initializeConnectorsFromTemplate(stationTemplate)
-    } else if (stationTemplate?.Evses != null && stationTemplate?.Connectors == null) {
+    } else if (stationTemplate.Evses != null && stationTemplate.Connectors == null) {
       this.initializeEvsesFromTemplate(stationTemplate)
-    } else if (stationTemplate?.Evses != null && stationTemplate?.Connectors != null) {
+    } 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)
@@ -1443,52 +1503,53 @@ export class ChargingStation extends EventEmitter {
   }
 
   private initializeConnectorsFromTemplate (stationTemplate: ChargingStationTemplate): void {
-    if (stationTemplate?.Connectors == null && this.connectors.size === 0) {
+    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] == null) {
+    if (stationTemplate.Connectors?.[0] == null) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
         } with no connector id 0 configuration`
       )
     }
-    if (stationTemplate?.Connectors != null) {
+    if (stationTemplate.Connectors != null) {
       const { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors } =
         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')
       const connectorsConfigChanged =
-        this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash
-      if (this.connectors?.size === 0 || connectorsConfigChanged) {
+        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] == null ||
+              // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+              (stationTemplate.Connectors[connectorId] == null ||
                 !this.getUseConnectorId0(stationTemplate))
             ) {
               continue
             }
             const templateConnectorId =
-              connectorId > 0 && stationTemplate?.randomConnectors === true
-                ? getRandomInteger(templateMaxAvailableConnectors, 1)
+              connectorId > 0 && stationTemplate.randomConnectors === true
+                ? randomInt(1, templateMaxAvailableConnectors)
                 : connectorId
-            const connectorStatus = stationTemplate?.Connectors[templateConnectorId]
+            const connectorStatus = stationTemplate.Connectors[templateConnectorId]
             checkStationInfoConnectorStatus(
               templateConnectorId,
               connectorStatus,
               this.logPrefix(),
               this.templateFile
             )
-            this.connectors.set(connectorId, cloneObject<ConnectorStatus>(connectorStatus))
+            this.connectors.set(connectorId, clone<ConnectorStatus>(connectorStatus))
           }
           initializeConnectorsMapStatus(this.connectors, this.logPrefix())
           this.saveConnectorsStatus()
@@ -1510,48 +1571,48 @@ export class ChargingStation extends EventEmitter {
   }
 
   private initializeEvsesFromTemplate (stationTemplate: ChargingStationTemplate): void {
-    if (stationTemplate?.Evses == null && this.evses.size === 0) {
+    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] == null) {
+    if (stationTemplate.Evses?.[0] == null) {
       logger.warn(
         `${this.logPrefix()} Charging station information from template ${
           this.templateFile
         } with no evse id 0 configuration`
       )
     }
-    if (stationTemplate?.Evses?.[0]?.Connectors?.[0] == null) {
+    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`
       )
     }
-    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`
       )
     }
-    if (stationTemplate?.Evses != null) {
+    if (stationTemplate.Evses != null) {
       const evsesConfigHash = createHash(Constants.DEFAULT_HASH_ALGORITHM)
-        .update(JSON.stringify(stationTemplate?.Evses))
+        .update(JSON.stringify(stationTemplate.Evses))
         .digest('hex')
       const evsesConfigChanged =
-        this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash
-      if (this.evses?.size === 0 || 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)
+        const templateMaxEvses = getMaxNumberOfEvses(stationTemplate.Evses)
         if (templateMaxEvses > 0) {
           for (const evseKey in stationTemplate.Evses) {
             const evseId = convertToInt(evseKey)
             this.evses.set(evseId, {
               connectors: buildConnectorsMap(
-                stationTemplate?.Evses[evseKey]?.Connectors,
+                stationTemplate.Evses[evseKey].Connectors,
                 this.logPrefix(),
                 this.templateFile
               ),
@@ -1629,15 +1690,12 @@ export class ChargingStation extends EventEmitter {
         if (!existsSync(dirname(this.configurationFile))) {
           mkdirSync(dirname(this.configurationFile), { recursive: true })
         }
+        const configurationFromFile = this.getConfigurationFromFile()
         let configurationData: ChargingStationConfiguration =
-          this.getConfigurationFromFile() != null
-            ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-            cloneObject<ChargingStationConfiguration>(this.getConfigurationFromFile()!)
+          configurationFromFile != null
+            ? clone<ChargingStationConfiguration>(configurationFromFile)
             : {}
-        if (
-          this.stationInfo?.stationInfoPersistentConfiguration === true &&
-          this.stationInfo != null
-        ) {
+        if (this.stationInfo?.stationInfoPersistentConfiguration === true) {
           configurationData.stationInfo = this.stationInfo
         } else {
           delete configurationData.stationInfo
@@ -1646,18 +1704,15 @@ export class ChargingStation extends EventEmitter {
           this.stationInfo?.ocppPersistentConfiguration === true &&
           Array.isArray(this.ocppConfiguration?.configurationKey)
         ) {
-          configurationData.configurationKey = this.ocppConfiguration?.configurationKey
+          configurationData.configurationKey = this.ocppConfiguration.configurationKey
         } else {
           delete configurationData.configurationKey
         }
-        configurationData = merge<ChargingStationConfiguration>(
+        configurationData = mergeDeepRight(
           configurationData,
           buildChargingStationAutomaticTransactionGeneratorConfiguration(this)
         )
-        if (
-          this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration === false ||
-          this.getAutomaticTransactionGeneratorConfiguration() == null
-        ) {
+        if (this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration !== true) {
           delete configurationData.automaticTransactionGenerator
         }
         if (this.connectors.size > 0) {
@@ -1698,7 +1753,7 @@ export class ChargingStation extends EventEmitter {
             this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash)
             this.sharedLRUCache.setChargingStationConfiguration(configurationData)
             this.configurationFileHash = configurationHash
-          }).catch((error) => {
+          }).catch((error: unknown) => {
             handleFileException(
               this.configurationFile,
               FileType.ChargingStationConfiguration,
@@ -1732,17 +1787,21 @@ export class ChargingStation extends EventEmitter {
     return this.getTemplateFromFile()?.Configuration
   }
 
-  private getOcppConfigurationFromFile (): ChargingStationOcppConfiguration | undefined {
+  private getOcppConfigurationFromFile (
+    ocppPersistentConfiguration?: boolean
+  ): ChargingStationOcppConfiguration | undefined {
     const configurationKey = this.getConfigurationFromFile()?.configurationKey
-    if (this.stationInfo?.ocppPersistentConfiguration === true && Array.isArray(configurationKey)) {
+    if (ocppPersistentConfiguration === true && Array.isArray(configurationKey)) {
       return { configurationKey }
     }
     return undefined
   }
 
-  private getOcppConfiguration (): ChargingStationOcppConfiguration | undefined {
+  private getOcppConfiguration (
+    ocppPersistentConfiguration: boolean | undefined = this.stationInfo?.ocppPersistentConfiguration
+  ): ChargingStationOcppConfiguration | undefined {
     let ocppConfiguration: ChargingStationOcppConfiguration | undefined =
-      this.getOcppConfigurationFromFile()
+      this.getOcppConfigurationFromFile(ocppPersistentConfiguration)
     if (ocppConfiguration == null) {
       ocppConfiguration = this.getOcppConfigurationFromTemplate()
     }
@@ -1751,8 +1810,9 @@ export class ChargingStation extends EventEmitter {
 
   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`
+        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.href} succeeded`
       )
       let registrationRetryCount = 0
       if (!this.isRegistered()) {
@@ -1764,10 +1824,18 @@ export class ChargingStation extends EventEmitter {
           >(this, RequestCommand.BOOT_NOTIFICATION, this.bootNotificationRequest, {
             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 != null
+              // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+              this.bootNotificationResponse?.interval != null
                 ? secondsToMilliseconds(this.bootNotificationResponse.interval)
                 : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL
             )
@@ -1775,7 +1843,7 @@ export class ChargingStation extends EventEmitter {
         } while (
           !this.isRegistered() &&
           // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-          (registrationRetryCount <= this.stationInfo.registrationMaxRetries! ||
+          (registrationRetryCount <= this.stationInfo!.registrationMaxRetries! ||
             this.stationInfo?.registrationMaxRetries === -1)
         )
       }
@@ -1785,21 +1853,27 @@ export class ChargingStation extends EventEmitter {
           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.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:
@@ -1809,7 +1883,7 @@ export class ChargingStation extends EventEmitter {
             code
           )}' and reason '${reason.toString()}'`
         )
-        this.autoReconnectRetryCount = 0
+        this.wsConnectionRetryCount = 0
         break
       // Abnormal close
       default:
@@ -1818,13 +1892,22 @@ export class ChargingStation extends EventEmitter {
             code
           )}' and reason '${reason.toString()}'`
         )
-        this.started && (await this.reconnect())
+        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 {
+  private getCachedRequest (
+    messageType: MessageType | undefined,
+    messageId: string
+  ): CachedRequest | undefined {
     const cachedRequest = this.requests.get(messageId)
     if (Array.isArray(cachedRequest)) {
       return cachedRequest
@@ -1877,9 +1960,9 @@ export class ChargingStation extends EventEmitter {
       messageId
     )!
     logger.debug(
-      `${this.logPrefix()} << Command '${
-        requestCommandName ?? Constants.UNKNOWN_COMMAND
-      }' received response payload: ${JSON.stringify(response)}`
+      `${this.logPrefix()} << Command '${requestCommandName}' received response payload: ${JSON.stringify(
+        response
+      )}`
     )
     responseCallback(commandPayload, requestPayload)
   }
@@ -1898,9 +1981,9 @@ export class ChargingStation extends EventEmitter {
     // 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)}`
+      `${this.logPrefix()} << Command '${requestCommandName}' received error response payload: ${JSON.stringify(
+        errorResponse
+      )}`
     )
     errorCallback(new OCPPError(errorType, errorMessage, requestCommandName, errorDetails))
   }
@@ -1946,11 +2029,14 @@ export class ChargingStation extends EventEmitter {
         )
       }
     } catch (error) {
+      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
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      const [, messageId] = request!
+      const [, messageId] = request
       switch (messageType) {
         case MessageType.CALL_MESSAGE:
           [, , commandName] = request as IncomingRequest
@@ -1973,7 +2059,7 @@ export class ChargingStation extends EventEmitter {
       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
@@ -1981,11 +2067,11 @@ export class ChargingStation extends EventEmitter {
       }
       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
@@ -2006,25 +2092,31 @@ export class ChargingStation extends EventEmitter {
     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
-          ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-          Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue!)
+          ? connectorStatus?.transactionEnergyActiveImportRegisterValue != null
+            ? Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue)
+            : undefined
           : connectorStatus?.transactionEnergyActiveImportRegisterValue) ?? 0
       )
     }
     return (
       (rounded
-        ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        Math.round(connectorStatus.energyActiveImportRegisterValue!)
+        ? connectorStatus?.energyActiveImportRegisterValue != null
+          ? Math.round(connectorStatus.energyActiveImportRegisterValue)
+          : undefined
         : connectorStatus?.energyActiveImportRegisterValue) ?? 0
     )
   }
 
   private getUseConnectorId0 (stationTemplate?: ChargingStationTemplate): boolean {
-    return stationTemplate?.useConnectorId0 ?? true
+    // 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> {
@@ -2052,8 +2144,7 @@ export class ChargingStation extends EventEmitter {
   private getConnectionTimeout (): number {
     if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut) != null) {
       return convertToInt(
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)!.value! ??
+        getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)?.value ??
           Constants.DEFAULT_CONNECTION_TIMEOUT
       )
     }
@@ -2070,7 +2161,7 @@ export class ChargingStation extends EventEmitter {
 
   private getMaximumAmperage (stationInfo?: ChargingStationInfo): number | undefined {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    const maximumPower = (stationInfo ?? this.stationInfo).maximumPower!
+    const maximumPower = (stationInfo ?? this.stationInfo!).maximumPower!
     switch (this.getCurrentOutType(stationInfo)) {
       case CurrentType.AC:
         return ACElectricUtils.amperagePerPhaseFromPower(
@@ -2084,12 +2175,18 @@ export class ChargingStation extends EventEmitter {
   }
 
   private getCurrentOutType (stationInfo?: ChargingStationInfo): CurrentType {
-    return (stationInfo ?? this.stationInfo).currentOutType ?? CurrentType.AC
+    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 {
     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)
     )
   }
@@ -2097,19 +2194,16 @@ export class ChargingStation extends EventEmitter {
   private getAmperageLimitation (): number | undefined {
     if (
       isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) &&
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) != null
+      getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey) != null
     ) {
       return (
-        convertToInt(
-          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-          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,
@@ -2145,7 +2239,7 @@ export class ChargingStation extends EventEmitter {
         }
       }
     }
-    if (this.stationInfo.firmwareStatus === FirmwareStatus.Installing) {
+    if (this.stationInfo?.firmwareStatus === FirmwareStatus.Installing) {
       await this.ocppRequestService.requestHandler<
       FirmwareStatusNotificationRequest,
       FirmwareStatusNotificationResponse
@@ -2156,16 +2250,13 @@ export class ChargingStation extends EventEmitter {
     }
 
     // Start the ATG
-    if (this.getAutomaticTransactionGeneratorConfiguration().enable) {
-      this.startAutomaticTransactionGenerator()
+    if (this.getAutomaticTransactionGeneratorConfiguration()?.enable === true) {
+      this.startAutomaticTransactionGenerator(undefined, ATGStopAbsoluteDuration)
     }
     this.flushMessageBuffer()
   }
 
-  private async stopMessageSequence (
-    reason?: StopTransactionReason,
-    stopTransactions = this.stationInfo?.stopTransactionsOnStopped
-  ): Promise<void> {
+  private internalStopMessageSequence (): void {
     // Stop WebSocket ping
     this.stopWebSocketPing()
     // Stop heartbeat
@@ -2174,40 +2265,33 @@ export class ChargingStation extends EventEmitter {
     if (this.automaticTransactionGenerator?.started === true) {
       this.stopAutomaticTransactionGenerator()
     }
+  }
+
+  private async stopMessageSequence (
+    reason?: StopTransactionReason,
+    stopTransactions?: boolean
+  ): Promise<void> {
+    this.internalStopMessageSequence()
     // Stop ongoing transactions
     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,
-              buildStatusNotificationRequest(
-                this,
-                connectorId,
-                ConnectorStatusEnum.Unavailable,
-                evseId
-              )
+              connectorId,
+              ConnectorStatusEnum.Unavailable,
+              evseId
             )
-            delete connectorStatus?.status
+            delete connectorStatus.status
           }
         }
       }
     } else {
       for (const connectorId of this.connectors.keys()) {
         if (connectorId > 0) {
-          await this.ocppRequestService.requestHandler<
-          StatusNotificationRequest,
-          StatusNotificationResponse
-          >(
-            this,
-            RequestCommand.STATUS_NOTIFICATION,
-            buildStatusNotificationRequest(this, connectorId, ConnectorStatusEnum.Unavailable)
-          )
+          await sendAndSetConnectorStatus(this, connectorId, ConnectorStatusEnum.Unavailable)
           delete this.getConnectorStatus(connectorId)?.status
         }
       }
@@ -2215,14 +2299,14 @@ export class ChargingStation extends EventEmitter {
   }
 
   private startWebSocketPing (): void {
-    const webSocketPingInterval: number =
+    const webSocketPingInterval =
       getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval) != null
         ? convertToInt(
           getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value
         )
         : 0
-    if (webSocketPingInterval > 0 && this.webSocketPingSetInterval == null) {
-      this.webSocketPingSetInterval = setInterval(() => {
+    if (webSocketPingInterval > 0 && this.wsPingSetInterval == null) {
+      this.wsPingSetInterval = setInterval(() => {
         if (this.isWebSocketConnectionOpened()) {
           this.wsConnection?.ping()
         }
@@ -2232,7 +2316,7 @@ export class ChargingStation extends EventEmitter {
           webSocketPingInterval
         )}`
       )
-    } else if (this.webSocketPingSetInterval != null) {
+    } else if (this.wsPingSetInterval != null) {
       logger.info(
         `${this.logPrefix()} WebSocket ping already started every ${formatDurationSeconds(
           webSocketPingInterval
@@ -2246,9 +2330,9 @@ export class ChargingStation extends EventEmitter {
   }
 
   private stopWebSocketPing (): void {
-    if (this.webSocketPingSetInterval != null) {
-      clearInterval(this.webSocketPingSetInterval)
-      delete this.webSocketPingSetInterval
+    if (this.wsPingSetInterval != null) {
+      clearInterval(this.wsPingSetInterval)
+      delete this.wsPingSetInterval
     }
   }
 
@@ -2259,9 +2343,7 @@ export class ChargingStation extends EventEmitter {
       let configuredSupervisionUrlIndex: number
       switch (Configuration.getSupervisionUrlDistribution()) {
         case SupervisionUrlDistribution.RANDOM:
-          configuredSupervisionUrlIndex = Math.floor(
-            secureRandom() * (supervisionUrls as string[]).length
-          )
+          configuredSupervisionUrlIndex = Math.floor(secureRandom() * supervisionUrls.length)
           break
         case SupervisionUrlDistribution.ROUND_ROBIN:
         case SupervisionUrlDistribution.CHARGING_STATION_AFFINITY:
@@ -2270,25 +2352,26 @@ export class ChargingStation extends EventEmitter {
             // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
             Configuration.getSupervisionUrlDistribution()!
           ) &&
-            logger.error(
+            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
+          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)
     }
     const errorMsg = 'No supervision url(s) configured'
     logger.error(`${this.logPrefix()} ${errorMsg}`)
-    throw new BaseError(`${errorMsg}`)
+    throw new BaseError(errorMsg)
   }
 
   private stopHeartbeat (): void {
@@ -2306,29 +2389,20 @@ export class ChargingStation extends EventEmitter {
   }
 
   private async reconnect (): Promise<void> {
-    // Stop WebSocket ping
-    this.stopWebSocketPing()
-    // Stop heartbeat
-    this.stopHeartbeat()
-    // Stop the ATG if needed
-    if (this.getAutomaticTransactionGeneratorConfiguration().stopOnConnectionFailure) {
-      this.stopAutomaticTransactionGenerator()
-    }
     if (
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      this.autoReconnectRetryCount < this.stationInfo.autoReconnectMaxRetries! ||
+      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)
+          ? exponentialDelay(this.wsConnectionRetryCount)
           : secondsToMilliseconds(this.getConnectionTimeout())
       const reconnectDelayWithdraw = 1000
       const reconnectTimeout =
-        reconnectDelay != null && reconnectDelay - reconnectDelayWithdraw > 0
-          ? reconnectDelay - reconnectDelayWithdraw
-          : 0
+        reconnectDelay - reconnectDelayWithdraw > 0 ? reconnectDelay - reconnectDelayWithdraw : 0
       logger.error(
         `${this.logPrefix()} WebSocket connection retry in ${roundTo(
           reconnectDelay,
@@ -2337,7 +2411,7 @@ export class ChargingStation extends EventEmitter {
       )
       await sleep(reconnectDelay)
       logger.error(
-        `${this.logPrefix()} WebSocket connection retry #${this.autoReconnectRetryCount.toString()}`
+        `${this.logPrefix()} WebSocket connection retry #${this.wsConnectionRetryCount.toString()}`
       )
       this.openWSConnection(
         {
@@ -2348,7 +2422,7 @@ export class ChargingStation extends EventEmitter {
     } else if (this.stationInfo?.autoReconnectMaxRetries !== -1) {
       logger.error(
         `${this.logPrefix()} WebSocket connection retries failure: maximum retries reached (${
-          this.autoReconnectRetryCount
+          this.wsConnectionRetryCount
         }) or retries disabled (${this.stationInfo?.autoReconnectMaxRetries})`
       )
     }