Enhanced logs in update charging station from template
MODIFY
src/migration/tasks/UpdateChargingStationTemplatesTask.ts
src/migration/tasks/UpdateChargingStationTemplatesTask.ts
-import global from './../../types/GlobalType';
-import OCPPUtils from '../../server/ocpp/utils/OCPPUtils';
+import { OCPPChangeConfigurationCommandResult, OCPPConfigurationStatus } from '../../types/ocpp/OCPPClient';
+import global, { ActionsResponse } from './../../types/GlobalType';
+
import ChargingStationStorage from '../../storage/mongodb/ChargingStationStorage';
-import TenantStorage from '../../storage/mongodb/TenantStorage';
-import { TemplateUpdateResult } from '../../types/ChargingStation';
-import { OCPPChangeConfigurationCommandResult } from '../../types/ocpp/OCPPClient';
-import { ServerAction } from '../../types/Server';
-import Tenant from '../../types/Tenant';
import Constants from '../../utils/Constants';
import Logging from '../../utils/Logging';
-import Utils from '../../utils/Utils';
import MigrationTask from '../MigrationTask';
+import OCPPUtils from '../../server/ocpp/utils/OCPPUtils';
+import { ServerAction } from '../../types/Server';
+import Tenant from '../../types/Tenant';
+import TenantStorage from '../../storage/mongodb/TenantStorage';
+import Utils from '../../utils/Utils';
const MODULE_NAME = 'UpdateChargingStationTemplatesTask';
@@ -37,7 +37,7 @@ export default class UpdateChargingStationTemplatesTask extends MigrationTask {
}
getVersion() {
- return '2.3';
+ return '2.4';
}
private async applyTemplateToChargingStations(tenant: Tenant) {
@@ -59,13 +59,6 @@ export default class UpdateChargingStationTemplatesTask extends MigrationTask {
// Update
for (const chargingStation of chargingStations.result) {
try {
- Logging.logDebug({
- tenantID: Constants.DEFAULT_TENANT,
- action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
- source: chargingStation.id,
- module: MODULE_NAME, method: 'applyTemplateToChargingStations',
- message: `Check template for '${chargingStation.id}' in Tenant '${tenant.name}' ('${tenant.subdomain}')`,
- });
const chargingStationTemplateUpdated =
await OCPPUtils.enrichChargingStationWithTemplate(tenant.id, chargingStation);
// Enrich
@@ -82,6 +75,25 @@ export default class UpdateChargingStationTemplatesTask extends MigrationTask {
chargingStationTemplateUpdated.capabilitiesUpdated ||
chargingStationTemplateUpdated.ocppUpdated ||
chargingStationUpdated) {
+ const sectionsUpdated = [];
+ if (chargingStationTemplateUpdated.technicalUpdated) {
+ sectionsUpdated.push('Technical');
+ }
+ if (chargingStationTemplateUpdated.ocppUpdated) {
+ sectionsUpdated.push('OCPP');
+ }
+ if (chargingStationTemplateUpdated.capabilitiesUpdated) {
+ sectionsUpdated.push('Capabilities');
+ }
+ Logging.logInfo({
+ tenantID: Constants.DEFAULT_TENANT,
+ source: chargingStation.id,
+ action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
+ module: MODULE_NAME, method: 'enrichChargingStationWithTemplate',
+ message: `Charging Station '${chargingStation.id}' updated with the following Template's section(s): ${sectionsUpdated.join(', ')}`,
+ detailedMessages: { chargingStationTemplateUpdated }
+ });
+ // Save
await ChargingStationStorage.saveChargingStation(tenant.id, chargingStation);
updated++;
// Retrieve OCPP params and update them if needed
@@ -91,12 +103,34 @@ export default class UpdateChargingStationTemplatesTask extends MigrationTask {
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
source: chargingStation.id,
module: MODULE_NAME, method: 'applyTemplateToChargingStations',
- message: `Apply new OCPP Parameters for '${chargingStation.id}' in Tenant '${tenant.name}' ('${tenant.subdomain}')`,
+ message: `Apply Template's OCPP Parameters for '${chargingStation.id}' in Tenant '${tenant.name}' ('${tenant.subdomain}')`,
+ });
+ // Request the latest configuration
+ const result = await Utils.promiseWithTimeout<OCPPChangeConfigurationCommandResult>(
+ 60 * 1000, OCPPUtils.requestAndSaveChargingStationOcppParameters(tenant.id, chargingStation),
+ 'Time out error (60s) in requesting OCPP params');
+ if (result.status !== OCPPConfigurationStatus.ACCEPTED) {
+ Logging.logError({
+ tenantID: Constants.DEFAULT_TENANT,
+ action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'applyTemplateToChargingStations',
+ message: `Cannot request OCPP Parameters from '${chargingStation.id}' in Tenant '${tenant.name}' ('${tenant.subdomain}')`,
});
- await Utils.promiseWithTimeout<OCPPChangeConfigurationCommandResult>(
- 60 * 1000, OCPPUtils.requestAndSaveChargingStationOcppParameters(
- tenant.id, chargingStation, chargingStationTemplateUpdated.ocppUpdated),
- 'Time out error with updating OCPP params');
+ continue;
+ }
+ // Update the OCPP parameters from the template
+ const updatedOcppParameters = await Utils.promiseWithTimeout<ActionsResponse>(
+ 60 * 1000, OCPPUtils.updateChargingStationTemplateOcppParameters(tenant.id, chargingStation),
+ 'Time out error (60s) in updating OCPP Parameters');
+ // Log
+ Utils.logActionsResponse(Constants.DEFAULT_TENANT, ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
+ MODULE_NAME, 'applyTemplateToChargingStations', updatedOcppParameters,
+ `{{inSuccess}} OCPP Parameter(s) were successfully synchronized, check details in the Tenant '${tenant.name}' ('${tenant.subdomain}')`,
+ `{{inError}} OCPP Parameter(s) failed to be synchronized, check details in the Tenant '${tenant.name}' ('${tenant.subdomain}')`,
+ `{{inSuccess}} OCPP Parameter(s) were successfully synchronized and {{inError}} failed to be synchronized, check details in the Tenant '${tenant.name}' ('${tenant.subdomain}')`,
+ 'All the OCPP Parameters are up to date'
+ );
}
}
} catch (error) {
@@ -105,7 +139,7 @@ export default class UpdateChargingStationTemplatesTask extends MigrationTask {
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
source: chargingStation.id,
module: MODULE_NAME, method: 'applyTemplateToChargingStations',
- message: `Template update error in Tenant '${tenant.name}': ${error.message}`,
+ message: `Template update error in Tenant '${tenant.name}' ('${tenant.subdomain}'): ${error.message}`,
detailedMessages: { error: error.message, stack: error.stack }
});
}
@@ -115,7 +149,7 @@ export default class UpdateChargingStationTemplatesTask extends MigrationTask {
tenantID: Constants.DEFAULT_TENANT,
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
module: MODULE_NAME, method: 'applyTemplateToChargingStations',
- message: `${updated} Charging Stations have been updated with Template in Tenant '${tenant.name}'`
+ message: `${updated} Charging Stations have been processed with Template in Tenant '${tenant.name}' ('${tenant.subdomain}')`
});
}
}
@@ -139,7 +173,7 @@ export default class UpdateChargingStationTemplatesTask extends MigrationTask {
tenantID: Constants.DEFAULT_TENANT,
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
module: MODULE_NAME, method: 'cleanUpChargingStationDBProps',
- message: `${result.modifiedCount} Charging Stations unused properties have been removed in Tenant '${tenant.name}'`
+ message: `${result.modifiedCount} Charging Stations unused properties have been removed in Tenant '${tenant.name}' ('${tenant.subdomain}')`
});
}
}

DELETE
src/server/ocpp/utils/OCPPConstants.ts
-
-const OCPPConstants = {
- DEFAULT_OCPP_16_CONFIGURATION: [
- { 'key': 'AllowOfflineTxForUnknownId', 'readonly': false, 'value': null },
- { 'key': 'AuthorizationCacheEnabled', 'readonly': false, 'value': null },
- { 'key': 'AuthorizeRemoteTxRequests', 'readonly': false, 'value': null },
- { 'key': 'BlinkRepeat', 'readonly': false, 'value': null },
- { 'key': 'ClockAlignedDataInterval', 'readonly': false, 'value': null },
- { 'key': 'ConnectionTimeOut', 'readonly': false, 'value': null },
- { 'key': 'GetConfigurationMaxKeys', 'readonly': false, 'value': null },
- { 'key': 'HeartbeatInterval', 'readonly': false, 'value': null },
- { 'key': 'LightIntensity', 'readonly': false, 'value': null },
- { 'key': 'LocalAuthorizeOffline', 'readonly': false, 'value': null },
- { 'key': 'LocalPreAuthorize', 'readonly': false, 'value': null },
- { 'key': 'MaxEnergyOnInvalidId', 'readonly': false, 'value': null },
- { 'key': 'MeterValuesAlignedData', 'readonly': false, 'value': null },
- { 'key': 'MeterValuesAlignedDataMaxLength', 'readonly': false, 'value': null },
- { 'key': 'MeterValuesSampledData', 'readonly': false, 'value': null },
- { 'key': 'MeterValuesSampledDataMaxLength', 'readonly': false, 'value': null },
- { 'key': 'MeterValueSampleInterval', 'readonly': false, 'value': null },
- { 'key': 'MinimumStatusDuration', 'readonly': false, 'value': null },
- { 'key': 'NumberOfConnectors', 'readonly': false, 'value': null },
- { 'key': 'ResetRetries', 'readonly': false, 'value': null },
- { 'key': 'ConnectorPhaseRotation', 'readonly': false, 'value': null },
- { 'key': 'ConnectorPhaseRotationMaxLength', 'readonly': false, 'value': null },
- { 'key': 'StopTransactionOnEVSideDisconnect', 'readonly': false, 'value': null },
- { 'key': 'StopTransactionOnInvalidId', 'readonly': false, 'value': null },
- { 'key': 'StopTxnAlignedData', 'readonly': false, 'value': null },
- { 'key': 'StopTxnAlignedDataMaxLength', 'readonly': false, 'value': null },
- { 'key': 'StopTxnSampledData', 'readonly': false, 'value': null },
- { 'key': 'StopTxnSampledDataMaxLength', 'readonly': false, 'value': null },
- { 'key': 'SupportedFeatureProfiles', 'readonly': false, 'value': null },
- { 'key': 'SupportedFeatureProfilesMaxLength', 'readonly': false, 'value': null },
- { 'key': 'TransactionMessageAttempts', 'readonly': false, 'value': null },
- { 'key': 'TransactionMessageRetryInterval', 'readonly': false, 'value': null },
- { 'key': 'UnlockConnectorOnEVSideDisconnect', 'readonly': false, 'value': null },
- { 'key': 'WebSocketPingInterval', 'readonly': false, 'value': null },
- { 'key': 'LocalAuthListEnabled', 'readonly': false, 'value': null },
- { 'key': 'LocalAuthListMaxLength', 'readonly': false, 'value': null },
- { 'key': 'SendLocalListMaxLength', 'readonly': false, 'value': null },
- { 'key': 'ReserveConnectorZeroSupported', 'readonly': false, 'value': null },
- { 'key': 'ChargeProfileMaxStackLevel', 'readonly': false, 'value': null },
- { 'key': 'ChargingScheduleAllowedChargingRateUnit', 'readonly': false, 'value': null },
- { 'key': 'ChargingScheduleMaxPeriods', 'readonly': false, 'value': null },
- { 'key': 'ConnectorSwitch3to1PhaseSupported', 'readonly': false, 'value': null },
- { 'key': 'MaxChargingProfilesInstalled', 'readonly': false, 'value': null }
- ]
-};
-
-export default OCPPConstants;

MODIFY
src/server/ocpp/utils/OCPPUtils.ts
src/server/ocpp/utils/OCPPUtils.ts
import { ActionsResponse, KeyValue } from '../../../types/GlobalType';
import { ChargingProfile, ChargingProfilePurposeType } from '../../../types/ChargingProfile';
-import ChargingStation, { ChargingStationCapabilities, ChargingStationOcppParameters, ChargingStationTemplate, TemplateUpdateResult } from '../../../types/ChargingStation';
+import ChargingStation, { ChargingStationCapabilities, ChargingStationOcppParameters, ChargingStationTemplate, OcppParameter, TemplateUpdateResult } from '../../../types/ChargingStation';
import { OCPPChangeConfigurationCommandParam, OCPPChangeConfigurationCommandResult, OCPPChargingProfileStatus, OCPPConfigurationStatus, OCPPGetConfigurationCommandParam } from '../../../types/ocpp/OCPPClient';
import BackendError from '../../../exception/BackendError';
@@ -10,7 +10,6 @@ import ChargingStationVendorFactory from '../../../integration/charging-station-
import Constants from '../../../utils/Constants';
import { InactivityStatus } from '../../../types/Transaction';
import Logging from '../../../utils/Logging';
-import OCPPConstants from './OCPPConstants';
import { OCPPNormalizedMeterValue } from '../../../types/ocpp/OCPPServer';
import { ServerAction } from '../../../types/Server';
import SiteArea from '../../../types/SiteArea';
@@ -530,17 +529,21 @@ export default class OCPPUtils {
// Set default?
if (!chargingStationOcppParameters.configuration) {
// Check if there is an already existing config in DB
- const existingConfiguration = await ChargingStationStorage.getOcppParameters(tenantID, chargingStation.id);
- if (!existingConfiguration) {
+ const ocppParametersFromDB = await ChargingStationStorage.getOcppParameters(tenantID, chargingStation.id);
+ if (ocppParametersFromDB.count === 0) {
// No config at all: Set default OCPP configuration
- chargingStationOcppParameters.configuration = OCPPConstants.DEFAULT_OCPP_16_CONFIGURATION;
+ chargingStationOcppParameters.configuration = Constants.DEFAULT_OCPP_16_CONFIGURATION;
+ } else {
+ // Set DB
+ chargingStationOcppParameters.configuration = ocppParametersFromDB.result;
}
}
// Save config
await ChargingStationStorage.saveOcppParameters(tenantID, chargingStationOcppParameters);
// Check OCPP Configuration
if (forceUpdateOcppParametersWithTemplate) {
- await this.checkAndUpdateChargingStationOcppParameters(tenantID, chargingStation, chargingStationOcppParameters);
+ await this.updateChargingStationTemplateOcppParameters(
+ tenantID, chargingStation, chargingStationOcppParameters.configuration);
}
// Ok
Logging.logInfo({
@@ -558,17 +561,30 @@ export default class OCPPUtils {
}
}
- public static async checkAndUpdateChargingStationOcppParameters(tenantID: string, chargingStation: ChargingStation, currentParameters: ChargingStationOcppParameters) {
- let oneOcppParameterUpdated = false;
+ public static async updateChargingStationTemplateOcppParameters(tenantID: string, chargingStation: ChargingStation,
+ currentOcppParameters?: OcppParameter[]): Promise<ActionsResponse> {
+ const updatedOcppParams = {
+ inError: 0,
+ inSuccess: 0,
+ };
+ // Not Provided: Get from DB
+ if (!currentOcppParameters) {
+ // Check if there is an already existing config in DB
+ const ocppParametersFromDB = await ChargingStationStorage.getOcppParameters(tenantID, chargingStation.id);
+ if (ocppParametersFromDB.count > 0) {
+ currentOcppParameters = ocppParametersFromDB.result;
+ }
+ }
+ // Check
if (Utils.isEmptyArray(chargingStation.ocppStandardParameters) && Utils.isEmptyArray(chargingStation.ocppVendorParameters)) {
Logging.logInfo({
tenantID: tenantID,
source: chargingStation.id,
action: ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION,
- module: MODULE_NAME, method: 'checkAndUpdateChargingStationOcppParameters',
- message: 'Charging Station has no Standard/Vendor OCPP Parameters to change'
+ module: MODULE_NAME, method: 'updateChargingStationTemplateOcppParameters',
+ message: 'Charging Station has no OCPP Parameters'
});
- return;
+ return updatedOcppParams;
}
// Get the Charging Station client
const chargingStationClient = await ChargingStationClientFactory.getChargingStationClient(tenantID, chargingStation);
@@ -576,7 +592,7 @@ export default class OCPPUtils {
throw new BackendError({
source: chargingStation.id,
action: ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION,
- module: MODULE_NAME, method: 'checkAndUpdateChargingStationOcppParameters',
+ module: MODULE_NAME, method: 'updateChargingStationTemplateOcppParameters',
message: 'Charging Station is not connected to the backend',
});
}
@@ -585,7 +601,7 @@ export default class OCPPUtils {
// Check Standard OCPP Params
for (const ocppParameter of ocppParameters) {
// Find OCPP Param
- const currentOcppParam: KeyValue = currentParameters.configuration.find(
+ const currentOcppParam: KeyValue = currentOcppParameters.find(
(ocppParam) => ocppParam.key === ocppParameter.key);
try {
if (!currentOcppParam) {
@@ -594,9 +610,10 @@ export default class OCPPUtils {
tenantID: tenantID,
source: chargingStation.id,
action: ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION,
- module: MODULE_NAME, method: 'checkAndUpdateChargingStationOcppParameters',
+ module: MODULE_NAME, method: 'updateChargingStationTemplateOcppParameters',
message: `OCPP Parameter '${ocppParameter.key}' not found in Charging Station's configuration`
});
+ updatedOcppParams.inError++;
}
// Check Value
if (ocppParameter.value === currentOcppParam.value) {
@@ -605,7 +622,7 @@ export default class OCPPUtils {
tenantID: tenantID,
source: chargingStation.id,
action: ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION,
- module: MODULE_NAME, method: 'checkAndUpdateChargingStationOcppParameters',
+ module: MODULE_NAME, method: 'updateChargingStationTemplateOcppParameters',
message: `OCPP Parameter '${ocppParameter.key}' has the correct value '${currentOcppParam.value}'`
});
continue;
@@ -617,39 +634,42 @@ export default class OCPPUtils {
});
if (result.status === OCPPConfigurationStatus.ACCEPTED) {
// Ok
- oneOcppParameterUpdated = true;
+ updatedOcppParams.inSuccess++;
// Value is different: Update it
Logging.logInfo({
tenantID: tenantID,
source: chargingStation.id,
action: ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION,
- module: MODULE_NAME, method: 'checkAndUpdateChargingStationOcppParameters',
+ module: MODULE_NAME, method: 'updateChargingStationTemplateOcppParameters',
message: `OCPP Parameter '${currentOcppParam.key}' has been successfully set from '${currentOcppParam.value}' to '${ocppParameter.value}'`
});
} else {
+ updatedOcppParams.inError++;
Logging.logError({
tenantID: tenantID,
source: chargingStation.id,
action: ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION,
- module: MODULE_NAME, method: 'checkAndUpdateChargingStationOcppParameters',
+ module: MODULE_NAME, method: 'updateChargingStationTemplateOcppParameters',
message: `Error '${result.status}' in changing OCPP parameter '${ocppParameter.key}' from '${currentOcppParam.value}' to '${ocppParameter.value}': `
});
}
} catch (error) {
+ updatedOcppParams.inError++;
Logging.logError({
tenantID: tenantID,
source: chargingStation.id,
action: ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION,
- module: MODULE_NAME, method: 'checkAndUpdateChargingStationOcppParameters',
+ module: MODULE_NAME, method: 'updateChargingStationTemplateOcppParameters',
message: `Error in changing OCPP parameter '${ocppParameter.key}' from '${currentOcppParam.value}' to '${ocppParameter.value}'`,
detailedMessages: { error: error.message, stack: error.stack }
});
}
}
// Parameter Updated?
- if (oneOcppParameterUpdated) {
+ if (updatedOcppParams.inSuccess) {
await this.requestAndSaveChargingStationOcppParameters(tenantID, chargingStation);
}
+ return updatedOcppParams;
}
public static async requestChangeChargingStationOcppParameters(

MODIFY
src/storage/mongodb/ChargingStationStorage.ts
src/storage/mongodb/ChargingStationStorage.ts
@@ -602,23 +602,9 @@ export default class ChargingStationStorage {
// Read DB
const parametersMDB = await global.database.getCollection<ChargingStationOcppParameters>(tenantID, 'configurations')
.findOne({ '_id': id });
- // Found?
- const parameters: OcppParameter[] = [];
- if (parametersMDB && parametersMDB.configuration && parametersMDB.configuration.length > 0) {
- // Set values
- let index = 0;
- for (const parameter of parametersMDB.configuration) {
- parameters.push({
- id: index.toString(),
- key: parameter.key,
- value: parameter.value,
- readonly: parameter.readonly
- });
- index++;
- }
- }
// Sort
- parameters.sort((param1, param2) => {
+ if (parametersMDB.configuration) {
+ parametersMDB.configuration.sort((param1, param2) => {
if (param1.key.toLocaleLowerCase() < param2.key.toLocaleLowerCase()) {
return -1;
}
@@ -627,11 +613,12 @@ export default class ChargingStationStorage {
}
return 0;
});
+ }
// Debug
Logging.traceEnd(MODULE_NAME, 'getOcppParameters', uniqueTimerID);
return {
- count: parameters.length,
- result: parameters
+ count: parametersMDB.configuration.length,
+ result: parametersMDB.configuration
};
}

MODIFY
src/types/ChargingStation.ts
src/types/ChargingStation.ts
@@ -232,11 +232,10 @@ export interface ChargingStationCapabilities {
export interface ChargingStationOcppParameters {
id: string;
timestamp: Date;
- configuration: KeyValue[];
+ configuration: OcppParameter[];
}
export interface OcppParameter {
- id: string;
key: string;
value: string;
readonly: boolean;

MODIFY
src/types/ocpp/OCPPClient.ts
src/types/ocpp/OCPPClient.ts
import { ChargingProfile, ChargingRateUnitType, ChargingSchedule, Profile } from '../../types/ChargingProfile';
import { KeyValue } from '../GlobalType';
+import { OcppParameter } from '../ChargingStation';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface OCPPCommandParam {
@@ -43,7 +44,7 @@ export interface OCPPGetConfigurationCommandParam extends OCPPCommandParam {
}
export interface OCPPGetConfigurationCommandResult {
- configurationKey: KeyValue[];
+ configurationKey: OcppParameter[];
unknownKey?: string[];
}

MODIFY
src/utils/Constants.ts
src/utils/Constants.ts
+import { OcppParameter } from '../types/ChargingStation';
import Tenant from '../types/Tenant';
export default class Constants {
@@ -99,4 +100,50 @@ export default class Constants {
'plateID': 0,
'verificationToken': 0
};
+
+ public static readonly DEFAULT_OCPP_16_CONFIGURATION: OcppParameter[] = [
+ { 'key': 'AllowOfflineTxForUnknownId', 'readonly': false, 'value': null },
+ { 'key': 'AuthorizationCacheEnabled', 'readonly': false, 'value': null },
+ { 'key': 'AuthorizeRemoteTxRequests', 'readonly': false, 'value': null },
+ { 'key': 'BlinkRepeat', 'readonly': false, 'value': null },
+ { 'key': 'ClockAlignedDataInterval', 'readonly': false, 'value': null },
+ { 'key': 'ConnectionTimeOut', 'readonly': false, 'value': null },
+ { 'key': 'GetConfigurationMaxKeys', 'readonly': false, 'value': null },
+ { 'key': 'HeartbeatInterval', 'readonly': false, 'value': null },
+ { 'key': 'LightIntensity', 'readonly': false, 'value': null },
+ { 'key': 'LocalAuthorizeOffline', 'readonly': false, 'value': null },
+ { 'key': 'LocalPreAuthorize', 'readonly': false, 'value': null },
+ { 'key': 'MaxEnergyOnInvalidId', 'readonly': false, 'value': null },
+ { 'key': 'MeterValuesAlignedData', 'readonly': false, 'value': null },
+ { 'key': 'MeterValuesAlignedDataMaxLength', 'readonly': false, 'value': null },
+ { 'key': 'MeterValuesSampledData', 'readonly': false, 'value': null },
+ { 'key': 'MeterValuesSampledDataMaxLength', 'readonly': false, 'value': null },
+ { 'key': 'MeterValueSampleInterval', 'readonly': false, 'value': null },
+ { 'key': 'MinimumStatusDuration', 'readonly': false, 'value': null },
+ { 'key': 'NumberOfConnectors', 'readonly': false, 'value': null },
+ { 'key': 'ResetRetries', 'readonly': false, 'value': null },
+ { 'key': 'ConnectorPhaseRotation', 'readonly': false, 'value': null },
+ { 'key': 'ConnectorPhaseRotationMaxLength', 'readonly': false, 'value': null },
+ { 'key': 'StopTransactionOnEVSideDisconnect', 'readonly': false, 'value': null },
+ { 'key': 'StopTransactionOnInvalidId', 'readonly': false, 'value': null },
+ { 'key': 'StopTxnAlignedData', 'readonly': false, 'value': null },
+ { 'key': 'StopTxnAlignedDataMaxLength', 'readonly': false, 'value': null },
+ { 'key': 'StopTxnSampledData', 'readonly': false, 'value': null },
+ { 'key': 'StopTxnSampledDataMaxLength', 'readonly': false, 'value': null },
+ { 'key': 'SupportedFeatureProfiles', 'readonly': false, 'value': null },
+ { 'key': 'SupportedFeatureProfilesMaxLength', 'readonly': false, 'value': null },
+ { 'key': 'TransactionMessageAttempts', 'readonly': false, 'value': null },
+ { 'key': 'TransactionMessageRetryInterval', 'readonly': false, 'value': null },
+ { 'key': 'UnlockConnectorOnEVSideDisconnect', 'readonly': false, 'value': null },
+ { 'key': 'WebSocketPingInterval', 'readonly': false, 'value': null },
+ { 'key': 'LocalAuthListEnabled', 'readonly': false, 'value': null },
+ { 'key': 'LocalAuthListMaxLength', 'readonly': false, 'value': null },
+ { 'key': 'SendLocalListMaxLength', 'readonly': false, 'value': null },
+ { 'key': 'ReserveConnectorZeroSupported', 'readonly': false, 'value': null },
+ { 'key': 'ChargeProfileMaxStackLevel', 'readonly': false, 'value': null },
+ { 'key': 'ChargingScheduleAllowedChargingRateUnit', 'readonly': false, 'value': null },
+ { 'key': 'ChargingScheduleMaxPeriods', 'readonly': false, 'value': null },
+ { 'key': 'ConnectorSwitch3to1PhaseSupported', 'readonly': false, 'value': null },
+ { 'key': 'MaxChargingProfilesInstalled', 'readonly': false, 'value': null }
+ ];
}

Enhanced logs in update charging station from template