CF: include TS sources.
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index 4c538d7704f839c2b330004f61b624dd2cb3747c..44f1c9c55d48d0e3a520567946edf24229201a10 100644 (file)
@@ -15,24 +15,24 @@ import logger from '../utils/Logger';
 
 export default class ChargingStation {
   private _index: number;
-  private _stationTemplateFile;
+  private _stationTemplateFile: string;
   private _stationInfo;
   private _bootNotificationMessage;
   private _connectors;
   private _configuration;
-  private _connectorsConfigurationHash;
+  private _connectorsConfigurationHash: string;
   private _supervisionUrl;
   private _wsConnectionUrl;
-  private _wsConnection;
+  private _wsConnection: WebSocket;
   private _isSocketRestart;
-  private _autoReconnectRetryCount;
-  private _autoReconnectMaxRetries;
-  private _autoReconnectTimeout;
+  private _autoReconnectRetryCount: number;
+  private _autoReconnectMaxRetries: number;
+  private _autoReconnectTimeout: number;
   private _requests;
-  private _messageQueue;
+  private _messageQueue: any[];
   private _automaticTransactionGeneration: AutomaticTransactionGenerator;
   private _authorizedTags: string[];
-  private _heartbeatInterval;
+  private _heartbeatInterval: number;
   private _heartbeatSetInterval;
   private _statistics: Statistics;
   private _performanceObserver: PerformanceObserver;
@@ -169,12 +169,12 @@ export default class ChargingStation {
     return this._stationInfo.Configuration ? this._stationInfo.Configuration : {};
   }
 
-  _getAuthorizationFile() {
+  _getAuthorizationFile() : string {
     return this._stationInfo.authorizationFile && this._stationInfo.authorizationFile;
   }
 
   _loadAndGetAuthorizedTags(): string[] {
-    let authorizedTags = [];
+    let authorizedTags: string[] = [];
     const authorizationFile = this._getAuthorizationFile();
     if (authorizationFile) {
       try {
@@ -192,16 +192,16 @@ export default class ChargingStation {
     return authorizedTags;
   }
 
-  getRandomTagId() {
+  getRandomTagId(): string {
     const index = Math.floor(Math.random() * this._authorizedTags.length);
     return this._authorizedTags[index];
   }
 
-  hasAuthorizedTags() {
+  hasAuthorizedTags(): boolean {
     return !Utils.isEmptyArray(this._authorizedTags);
   }
 
-  getEnableStatistics() {
+  getEnableStatistics(): boolean {
     return !Utils.isUndefined(this._stationInfo.enableStatistics) ? Utils.convertToBoolean(this._stationInfo.enableStatistics) : true;
   }
 
@@ -214,7 +214,7 @@ export default class ChargingStation {
     }
   }
 
-  _getNumberOfRunningTransactions() {
+  _getNumberOfRunningTransactions(): number {
     let trxCount = 0;
     for (const connector in this._connectors) {
       if (this.getConnector(Utils.convertToInt(connector)).transactionStarted) {
@@ -224,7 +224,7 @@ export default class ChargingStation {
     return trxCount;
   }
 
-  _getPowerDivider() {
+  _getPowerDivider(): number {
     let powerDivider = this._getNumberOfConnectors();
     if (this._stationInfo.powerSharedByConnectors) {
       powerDivider = this._getNumberOfRunningTransactions();
@@ -236,11 +236,11 @@ export default class ChargingStation {
     return this._connectors[id];
   }
 
-  _getTemplateMaxNumberOfConnectors() {
+  _getTemplateMaxNumberOfConnectors(): number {
     return Object.keys(this._stationInfo.Connectors).length;
   }
 
-  _getMaxNumberOfConnectors() {
+  _getMaxNumberOfConnectors(): number {
     let maxConnectors = 0;
     if (!Utils.isEmptyArray(this._stationInfo.numberOfConnectors)) {
       // Distribute evenly the number of connectors
@@ -257,7 +257,7 @@ export default class ChargingStation {
     return this._connectors[0] ? Object.keys(this._connectors).length - 1 : Object.keys(this._connectors).length;
   }
 
-  _getVoltageOut() {
+  _getVoltageOut(): number {
     const errMsg = `${this._logPrefix()} Unknown ${this._getPowerOutType()} powerOutType in template file ${this._stationTemplateFile}, cannot define default voltage out`;
     let defaultVoltageOut;
     switch (this._getPowerOutType()) {
@@ -274,11 +274,11 @@ export default class ChargingStation {
     return !Utils.isUndefined(this._stationInfo.voltageOut) ? Utils.convertToInt(this._stationInfo.voltageOut) : defaultVoltageOut;
   }
 
-  _getPowerOutType() {
+  _getPowerOutType(): string {
     return !Utils.isUndefined(this._stationInfo.powerOutType) ? this._stationInfo.powerOutType : 'AC';
   }
 
-  _getSupervisionURL() {
+  _getSupervisionURL(): string {
     const supervisionUrls = Utils.cloneObject(this._stationInfo.supervisionURL ? this._stationInfo.supervisionURL : Configuration.getSupervisionURLs());
     let indexUrl = 0;
     if (!Utils.isEmptyArray(supervisionUrls)) {
@@ -293,12 +293,12 @@ export default class ChargingStation {
     return supervisionUrls;
   }
 
-  _getAuthorizeRemoteTxRequests() {
+  _getAuthorizeRemoteTxRequests(): boolean {
     const authorizeRemoteTxRequests = this._getConfigurationKey('AuthorizeRemoteTxRequests');
     return authorizeRemoteTxRequests ? Utils.convertToBoolean(authorizeRemoteTxRequests.value) : false;
   }
 
-  _getLocalAuthListEnabled() {
+  _getLocalAuthListEnabled(): boolean {
     const localAuthListEnabled = this._getConfigurationKey('LocalAuthListEnabled');
     return localAuthListEnabled ? Utils.convertToBoolean(localAuthListEnabled.value) : false;
   }
@@ -332,7 +332,7 @@ export default class ChargingStation {
     }
   }
 
-  async _stopMessageSequence(reason = '') {
+  async _stopMessageSequence(reason = ''): Promise<void> {
     // Stop heartbeat
     this._stopHeartbeat();
     // Stop the ATG
@@ -360,15 +360,15 @@ export default class ChargingStation {
     }
   }
 
-  _stopHeartbeat() {
+  _stopHeartbeat(): void {
     if (this._heartbeatSetInterval) {
       clearInterval(this._heartbeatSetInterval);
       this._heartbeatSetInterval = null;
     }
   }
 
-  _startAuthorizationFileMonitoring() {
-    // eslint-disable-next-line no-unused-vars
+  _startAuthorizationFileMonitoring(): void {
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
     fs.watchFile(this._getAuthorizationFile(), (current, previous) => {
       try {
         logger.debug(this._logPrefix() + ' Authorization file ' + this._getAuthorizationFile() + ' have changed, reload');
@@ -380,8 +380,8 @@ export default class ChargingStation {
     });
   }
 
-  _startStationTemplateFileMonitoring() {
-    // eslint-disable-next-line no-unused-vars
+  _startStationTemplateFileMonitoring(): void {
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
     fs.watchFile(this._stationTemplateFile, (current, previous) => {
       try {
         logger.debug(this._logPrefix() + ' Template file ' + this._stationTemplateFile + ' have changed, reload');
@@ -406,17 +406,15 @@ export default class ChargingStation {
       return;
     }
     if (interval > 0) {
-      this.getConnector(connectorId).transactionSetInterval = setInterval(async () => {
-        // eslint-disable-next-line @typescript-eslint/no-this-alias
-        const self = this;
+      this.getConnector(connectorId).transactionSetInterval = setInterval(async (): Promise<void> => {
         if (this.getEnableStatistics()) {
           const sendMeterValues = performance.timerify(this.sendMeterValues);
           this._performanceObserver.observe({
             entryTypes: ['function'],
           });
-          await sendMeterValues(connectorId, interval, self);
+          await sendMeterValues(connectorId, interval, this);
         } else {
-          await this.sendMeterValues(connectorId, interval, self);
+          await this.sendMeterValues(connectorId, interval, this);
         }
       }, interval);
     } else {
@@ -424,7 +422,7 @@ export default class ChargingStation {
     }
   }
 
-  start() {
+  start(): void {
     if (!this._wsConnectionUrl) {
       this._wsConnectionUrl = this._supervisionUrl + '/' + this._stationInfo.name;
     }
@@ -446,7 +444,7 @@ export default class ChargingStation {
     this._wsConnection.on('ping', this.onPing.bind(this));
   }
 
-  async stop(reason = '') {
+  async stop(reason = ''): Promise<void> {
     // Stop
     await this._stopMessageSequence();
     // eslint-disable-next-line guard-for-in
@@ -454,11 +452,11 @@ export default class ChargingStation {
       await this.sendStatusNotification(Utils.convertToInt(connector), 'Unavailable');
     }
     if (this._wsConnection && this._wsConnection.readyState === WebSocket.OPEN) {
-      await this._wsConnection.close();
+      this._wsConnection.close();
     }
   }
 
-  _reconnect(error) {
+  _reconnect(error): void {
     logger.error(this._logPrefix() + ' Socket: abnormally closed', error);
     // Stop the ATG if needed
     if (Utils.convertToBoolean(this._stationInfo.AutomaticTransactionGenerator.enable) &&
@@ -542,6 +540,9 @@ export default class ChargingStation {
       switch (messageType) {
         // Incoming Message
         case Constants.OCPP_JSON_CALL_MESSAGE:
+          if (this.getEnableStatistics()) {
+            this._statistics.addMessage(commandName, messageType);
+          }
           // Process the call
           await this.handleRequest(messageId, commandName, commandPayload);
           break;
@@ -557,7 +558,7 @@ export default class ChargingStation {
           }
           if (!responseCallback) {
             // Error
-            throw new Error(`Response for unknown message id ${messageId}`);
+            throw new Error(`Response request for unknown message id ${messageId}`);
           }
           delete this._requests[messageId];
           responseCallback(commandName, requestPayload);
@@ -566,7 +567,7 @@ export default class ChargingStation {
         case Constants.OCPP_JSON_CALL_ERROR_MESSAGE:
           if (!this._requests[messageId]) {
             // Error
-            throw new Error(`Error for unknown message id ${messageId}`);
+            throw new Error(`Error request for unknown message id ${messageId}`);
           }
           // eslint-disable-next-line no-case-declarations
           let rejectCallback;
@@ -580,38 +581,41 @@ export default class ChargingStation {
           break;
         // Error
         default:
-          throw new Error(`Wrong message type ${messageType}`);
+          // eslint-disable-next-line no-case-declarations
+          const errMsg = `${this._logPrefix()} Wrong message type ${messageType}`;
+          logger.error(errMsg);
+          throw new Error(errMsg);
       }
     } catch (error) {
       // Log
-      logger.error('%s Incoming message %j processing error %s on request content %s', this._logPrefix(), message, error, this._requests[messageId]);
+      logger.error('%s Incoming message %j processing error %s on request content type %s', this._logPrefix(), message, error, this._requests[messageId]);
       // Send error
-      // await this.sendError(messageId, error);
+      await this.sendError(messageId, error, commandName);
     }
   }
 
-  sendHeartbeat() {
+  async sendHeartbeat() {
     try {
       const payload = {
         currentTime: new Date().toISOString(),
       };
-      this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'Heartbeat');
+      await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'Heartbeat');
     } catch (error) {
       logger.error(this._logPrefix() + ' Send Heartbeat error: ' + error);
       throw error;
     }
   }
 
-  sendBootNotification() {
+  async sendBootNotification() {
     try {
-      this.sendMessage(Utils.generateUUID(), this._bootNotificationMessage, Constants.OCPP_JSON_CALL_MESSAGE, 'BootNotification');
+      await this.sendMessage(Utils.generateUUID(), this._bootNotificationMessage, Constants.OCPP_JSON_CALL_MESSAGE, 'BootNotification');
     } catch (error) {
       logger.error(this._logPrefix() + ' Send BootNotification error: ' + error);
       throw error;
     }
   }
 
-  async sendStatusNotification(connectorId: number, status, errorCode = 'NoError') {
+  async sendStatusNotification(connectorId: number, status: string, errorCode = 'NoError') {
     try {
       const payload = {
         connectorId,
@@ -625,7 +629,7 @@ export default class ChargingStation {
     }
   }
 
-  async sendStartTransaction(connectorId: number, idTag?: string) {
+  async sendStartTransaction(connectorId: number, idTag?: string): Promise<unknown> {
     try {
       const payload = {
         connectorId,
@@ -640,7 +644,7 @@ export default class ChargingStation {
     }
   }
 
-  async sendStopTransaction(transactionId, reason = ''): Promise<void> {
+  async sendStopTransaction(transactionId: number, reason = ''): Promise<void> {
     try {
       const payload = {
         transactionId,
@@ -656,7 +660,7 @@ export default class ChargingStation {
   }
 
   // eslint-disable-next-line consistent-this
-  async sendMeterValues(connectorId: number, interval: number, self, debug = false): Promise<void> {
+  async sendMeterValues(connectorId: number, interval: number, self: ChargingStation, debug = false): Promise<void> {
     try {
       const sampledValues = {
         timestamp: new Date().toISOString(),
@@ -680,12 +684,13 @@ export default class ChargingStation {
           }
         // Voltage measurand
         } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === 'Voltage' && self._getConfigurationKey('MeterValuesSampledData').value.includes('Voltage')) {
+          const voltageMeasurandValue = Utils.getRandomFloatRounded(self._getVoltageOut() + self._getVoltageOut() * 0.1, self._getVoltageOut() - self._getVoltageOut() * 0.1);
           sampledValues.sampledValue.push({
             ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: 'V' },
             ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
             measurand: meterValuesTemplate[index].measurand,
             ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
-            ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: self._getVoltageOut() },
+            ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue },
           });
           for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
             const voltageValue = sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value;
@@ -700,7 +705,7 @@ export default class ChargingStation {
               ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
               measurand: meterValuesTemplate[index].measurand,
               ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
-              ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: self._getVoltageOut() },
+              ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue },
               phase: phaseValue,
             });
           }
@@ -876,14 +881,15 @@ export default class ChargingStation {
     }
   }
 
-  sendError(messageId, err) {
-    // Check exception: only OCPP error are accepted
-    const error = err instanceof OCPPError ? err : new OCPPError(Constants.OCPP_ERROR_INTERNAL_ERROR, err.message);
+  async sendError(messageId, err: Error|OCPPError, commandName) {
+    // Check exception type: only OCPP error are accepted
+    const error = err instanceof OCPPError ? err : new OCPPError(Constants.OCPP_ERROR_INTERNAL_ERROR, err.message, err.stack && err.stack);
     // Send error
-    return this.sendMessage(messageId, error, Constants.OCPP_JSON_CALL_ERROR_MESSAGE);
+    return this.sendMessage(messageId, error, Constants.OCPP_JSON_CALL_ERROR_MESSAGE, commandName);
   }
 
-  sendMessage(messageId, command, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName = '') {
+  async sendMessage(messageId, commandParams, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName: string) {
+    // eslint-disable-next-line @typescript-eslint/no-this-alias
     const self = this;
     // Send a message through wsConnection
     return new Promise((resolve, reject) => {
@@ -892,61 +898,70 @@ export default class ChargingStation {
       switch (messageType) {
         // Request
         case Constants.OCPP_JSON_CALL_MESSAGE:
-          if (this.getEnableStatistics()) {
-            this._statistics.addMessage(commandName);
-          }
           // Build request
-          this._requests[messageId] = [responseCallback, rejectCallback, command];
-          messageToSend = JSON.stringify([messageType, messageId, commandName, command]);
+          this._requests[messageId] = [responseCallback, rejectCallback, commandParams];
+          messageToSend = JSON.stringify([messageType, messageId, commandName, commandParams]);
           break;
         // Response
         case Constants.OCPP_JSON_CALL_RESULT_MESSAGE:
-          if (this.getEnableStatistics()) {
-            this._statistics.addMessage(commandName);
-          }
           // Build response
-          messageToSend = JSON.stringify([messageType, messageId, command]);
+          messageToSend = JSON.stringify([messageType, messageId, commandParams]);
           break;
         // Error Message
         case Constants.OCPP_JSON_CALL_ERROR_MESSAGE:
-          if (this.getEnableStatistics()) {
-            this._statistics.addMessage(`Error ${command.code ? command.code : Constants.OCPP_ERROR_GENERIC_ERROR} on ${commandName}`);
-          }
-          // Build Message
-          messageToSend = JSON.stringify([messageType, messageId, command.code ? command.code : Constants.OCPP_ERROR_GENERIC_ERROR, command.message ? command.message : '', command.details ? command.details : {}]);
+          // Build Error Message
+          messageToSend = JSON.stringify([messageType, messageId, commandParams.code ? commandParams.code : Constants.OCPP_ERROR_GENERIC_ERROR, commandParams.message ? commandParams.message : '', commandParams.details ? commandParams.details : {}]);
           break;
       }
       // Check if wsConnection is ready
       if (this._wsConnection && this._wsConnection.readyState === WebSocket.OPEN) {
+        if (this.getEnableStatistics()) {
+          this._statistics.addMessage(commandName, messageType);
+        }
         // Yes: Send Message
         this._wsConnection.send(messageToSend);
       } else {
-        // Buffer message until connection is back
-        this._messageQueue.push(messageToSend);
+        let dups = false;
+        // Handle dups in buffer
+        for (const message of this._messageQueue) {
+          // Same message
+          if (JSON.stringify(messageToSend) === JSON.stringify(message)) {
+            dups = true;
+            break;
+          }
+        }
+        if (!dups) {
+          // Buffer message
+          this._messageQueue.push(messageToSend);
+        }
+        // Reject it
+        return rejectCallback(new OCPPError(commandParams.code ? commandParams.code : Constants.OCPP_ERROR_GENERIC_ERROR, commandParams.message ? commandParams.message : `Web socket closed for message id '${messageId}' with content '${messageToSend}', message buffered`, commandParams.details ? commandParams.details : {}));
       }
-      // Request?
-      if (messageType !== Constants.OCPP_JSON_CALL_MESSAGE) {
+      // Response?
+      if (messageType === Constants.OCPP_JSON_CALL_RESULT_MESSAGE) {
         // Yes: send Ok
         resolve();
-      } else if (this._wsConnection && this._wsConnection.readyState === WebSocket.OPEN) {
-        // Send timeout in case connection is open otherwise wait for ever
-        // FIXME: Handle message on timeout
-        setTimeout(() => rejectCallback(new OCPPError(command.code ? command.code : Constants.OCPP_ERROR_GENERIC_ERROR, command.message ? command.message : '', command.details ? command.details : {})), Constants.OCPP_SOCKET_TIMEOUT);
+      } else if (messageType === Constants.OCPP_JSON_CALL_ERROR_MESSAGE) {
+        // Send timeout
+        setTimeout(() => rejectCallback(new OCPPError(commandParams.code ? commandParams.code : Constants.OCPP_ERROR_GENERIC_ERROR, commandParams.message ? commandParams.message : `Timeout for message id '${messageId}' with content '${messageToSend}'`, commandParams.details ? commandParams.details : {})), Constants.OCPP_SOCKET_TIMEOUT);
       }
 
       // Function that will receive the request's response
-      function responseCallback(payload, requestPayload) {
-        self.handleResponse(commandName, payload, requestPayload);
+      function responseCallback(payload, requestPayload): void {
+        if (self.getEnableStatistics()) {
+          self._statistics.addMessage(commandName, messageType);
+        }
         // Send the response
+        self.handleResponse(commandName, payload, requestPayload);
         resolve(payload);
       }
 
       // Function that will receive the request's rejection
-      function rejectCallback(error: OCPPError) {
-        logger.debug(`${self._logPrefix()} Error %j on commandName %s command %j`, error, commandName, command);
+      function rejectCallback(error: OCPPError): void {
         if (self.getEnableStatistics()) {
-          self._statistics.addMessage(`Error on commandName ${commandName}`, true);
+          self._statistics.addMessage(commandName, messageType);
         }
+        logger.debug(`${self._logPrefix()} Error %j occurred when calling command %s with parameters %j`, error, commandName, commandParams);
         // Build Exception
         // eslint-disable-next-line no-empty-function
         self._requests[messageId] = [() => { }, () => { }, '']; // Properly format the request
@@ -956,10 +971,7 @@ export default class ChargingStation {
     });
   }
 
-  handleResponse(commandName, payload, requestPayload) {
-    if (this.getEnableStatistics()) {
-      this._statistics.addMessage(commandName, true);
-    }
+  handleResponse(commandName: string, payload, requestPayload) {
     const responseCallbackFn = 'handleResponse' + commandName;
     if (typeof this[responseCallbackFn] === 'function') {
       this[responseCallbackFn](payload, requestPayload);
@@ -1069,9 +1081,6 @@ export default class ChargingStation {
   }
 
   async handleRequest(messageId, commandName, commandPayload) {
-    if (this.getEnableStatistics()) {
-      this._statistics.addMessage(commandName, true);
-    }
     let response;
     // Call
     if (typeof this['handleRequest' + commandName] === 'function') {
@@ -1082,15 +1091,16 @@ export default class ChargingStation {
         // Log
         logger.error(this._logPrefix() + ' Handle request error: ' + error);
         // Send back response to inform backend
-        await this.sendError(messageId, error);
+        await this.sendError(messageId, error, commandName);
+        throw error;
       }
     } else {
       // Throw exception
-      await this.sendError(messageId, new OCPPError(Constants.OCPP_ERROR_NOT_IMPLEMENTED, 'Not implemented', {}));
+      await this.sendError(messageId, new OCPPError(Constants.OCPP_ERROR_NOT_IMPLEMENTED, `${commandName} is not implemented`, {}), commandName);
       throw new Error(`${commandName} is not implemented ${JSON.stringify(commandPayload, null, ' ')}`);
     }
     // Send response
-    await this.sendMessage(messageId, response, Constants.OCPP_JSON_CALL_RESULT_MESSAGE);
+    await this.sendMessage(messageId, response, Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName);
   }
 
   // Simulate charging station restart
@@ -1214,7 +1224,7 @@ export default class ChargingStation {
       // Check if authorized
       if (this._authorizedTags.find((value) => value === commandPayload.idTag)) {
         // Authorization successful start transaction
-        this.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
+        await this.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
         logger.debug(this._logPrefix() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID + ' for idTag ' + commandPayload.idTag);
         return Constants.OCPP_RESPONSE_ACCEPTED;
       }
@@ -1222,7 +1232,7 @@ export default class ChargingStation {
       return Constants.OCPP_RESPONSE_REJECTED;
     }
     // No local authorization check required => start transaction
-    this.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
+    await this.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
     logger.debug(this._logPrefix() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID + ' for idTag ' + commandPayload.idTag);
     return Constants.OCPP_RESPONSE_ACCEPTED;
   }
@@ -1230,7 +1240,7 @@ export default class ChargingStation {
   async handleRequestRemoteStopTransaction(commandPayload) {
     for (const connector in this._connectors) {
       if (this.getConnector(Utils.convertToInt(connector)).transactionId === commandPayload.transactionId) {
-        this.sendStopTransaction(commandPayload.transactionId);
+        await this.sendStopTransaction(commandPayload.transactionId);
         return Constants.OCPP_RESPONSE_ACCEPTED;
       }
     }