Finish the job 1/2
MODIFY
src/client/ocpi/EmspOCPIClient.ts
src/client/ocpi/EmspOCPIClient.ts
@@ -2,9 +2,6 @@ import { OCPIToken, OCPITokenWhitelist } from '../../types/ocpi/OCPIToken';
import BackendError from '../../exception/BackendError';
import ChargingStation from '../../types/ChargingStation';
-import ChargingStationStorage from '../../storage/mongodb/ChargingStationStorage';
-import Company from '../../types/Company';
-import CompanyStorage from '../../storage/mongodb/CompanyStorage';
import Constants from '../../utils/Constants';
import { DataResult } from '../../types/DataResult';
import Logging from '../../utils/Logging';
@@ -15,7 +12,6 @@ import { OCPICommandResponse } from '../../types/ocpi/OCPICommandResponse';
import { OCPICommandType } from '../../types/ocpi/OCPICommandType';
import OCPIEndpoint from '../../types/ocpi/OCPIEndpoint';
import OCPIEndpointStorage from '../../storage/mongodb/OCPIEndpointStorage';
-import { OCPIEvseStatus } from '../../types/ocpi/OCPIEvse';
import { OCPILocation } from '../../types/ocpi/OCPILocation';
import { OCPIResult } from '../../types/ocpi/OCPIResult';
import { OCPIRole } from '../../types/ocpi/OCPIRole';
@@ -27,9 +23,6 @@ import OCPIUtilsService from '../../server/ocpi/ocpi-services-impl/ocpi-2.1.1/OC
import { OcpiSetting } from '../../types/Setting';
import { Promise } from 'bluebird';
import { ServerAction } from '../../types/Server';
-import Site from '../../types/Site';
-import SiteArea from '../../types/SiteArea';
-import SiteAreaStorage from '../../storage/mongodb/SiteAreaStorage';
import SiteStorage from '../../storage/mongodb/SiteStorage';
import TagStorage from '../../storage/mongodb/TagStorage';
import Tenant from '../../types/Tenant';
@@ -119,20 +112,6 @@ export default class EmspOCPIClient extends OCPIClient {
return result;
}
- public async checkAndGetCompany(): Promise<Company> {
- let company = await CompanyStorage.getCompany(this.tenant, this.ocpiEndpoint.id);
- if (!company) {
- company = {
- id: this.ocpiEndpoint.id,
- name: `${this.ocpiEndpoint.name} (${this.ocpiEndpoint.role})`,
- issuer: false,
- createdOn: new Date()
- } as Company;
- await CompanyStorage.saveCompany(this.tenant, company, false);
- }
- return company;
- }
-
public async pullLocations(partial = true): Promise<OCPIResult> {
// Result
const result: OCPIResult = {
@@ -153,7 +132,7 @@ export default class EmspOCPIClient extends OCPIClient {
// Take them all
locationsUrl = `${locationsUrl}?limit=5`;
}
- const company = await this.checkAndGetCompany();
+ const company = await OCPIUtils.checkAndGetEMSPCompany(this.tenant, this.ocpiEndpoint);
const sites = await SiteStorage.getSites(this.tenant,
{ companyIDs: [ company.id ] }, Constants.DB_PARAMS_MAX_LIMIT);
let nextResult = true;
@@ -171,7 +150,7 @@ export default class EmspOCPIClient extends OCPIClient {
// Cannot process locations in parallel (uniqueness is on site name) -> leads to dups
for (const location of locations) {
try {
- await this.processLocation(location, company, sites.result);
+ await OCPIUtils.processEMSPLocation(this.tenant, location, company, sites.result);
result.success++;
} catch (error) {
result.failure++;
@@ -315,111 +294,6 @@ export default class EmspOCPIClient extends OCPIClient {
return result;
}
- public async processLocation(location: OCPILocation, company: Company, existingSites: Site[]): Promise<void> {
- // Handle Site
- let site: Site;
- const siteName = location.operator?.name ?? OCPIUtils.buildOperatorName(this.ocpiEndpoint.countryCode, this.ocpiEndpoint.partyId);
- site = existingSites.find((existingSite) => existingSite.name === siteName);
- if (!site) {
- // Create Site
- site = {
- name: siteName,
- createdOn: new Date(),
- companyID: company.id,
- issuer: false,
- address: {
- address1: location.address,
- postalCode: location.postal_code,
- city: location.city,
- country: location.country,
- coordinates: []
- }
- } as Site;
- if (location.coordinates?.latitude && location.coordinates?.longitude) {
- site.address.coordinates = [
- Utils.convertToFloat(location.coordinates.longitude),
- Utils.convertToFloat(location.coordinates.latitude)
- ];
- }
- site.id = await SiteStorage.saveSite(this.tenant, site, false);
- // Push the Site then it can be retrieve in the next round
- existingSites.push(site);
- }
- const locationName = site.name + Constants.OCPI_SEPARATOR + location.id;
- // Handle Site Area
- const siteAreas = await SiteAreaStorage.getSiteAreas(this.tenant,
- { siteIDs: [site.id], name: locationName, issuer: false, withSite: true },
- Constants.DB_PARAMS_SINGLE_RECORD);
- let siteArea = !Utils.isEmptyArray(siteAreas.result) ? siteAreas.result[0] : null;
- if (!siteArea) {
- // Create Site Area
- siteArea = {
- name: locationName,
- createdOn: new Date(),
- siteID: site.id,
- issuer: false,
- address: {
- address1: location.address,
- address2: location.name,
- postalCode: location.postal_code,
- city: location.city,
- country: location.country,
- coordinates: []
- }
- } as SiteArea;
- if (location.coordinates?.latitude && location.coordinates?.longitude) {
- siteArea.address.coordinates = [
- Utils.convertToFloat(location.coordinates.longitude),
- Utils.convertToFloat(location.coordinates.latitude)
- ];
- }
- siteArea.id = await SiteAreaStorage.saveSiteArea(this.tenant, siteArea, false);
- }
- if (!Utils.isEmptyArray(location.evses)) {
- for (const evse of location.evses) {
- if (!evse.uid) {
- throw new BackendError({
- action: ServerAction.OCPI_PULL_LOCATIONS,
- message: `Missing Charging Station EVSE UID in Location '${location.name}' with ID '${location.id}'`,
- module: MODULE_NAME, method: 'processLocation',
- detailedMessages: { evse, location }
- });
- }
- if (evse.status === OCPIEvseStatus.REMOVED) {
- // Get existing charging station
- const currentChargingStation = await ChargingStationStorage.getChargingStationByOcpiLocationUid(
- this.tenant, location.id, evse.uid, ['id']
- );
- if (currentChargingStation) {
- await ChargingStationStorage.deleteChargingStation(this.tenant, currentChargingStation.id);
- await Logging.logDebug({
- tenantID: this.tenant.id,
- action: ServerAction.OCPI_PULL_LOCATIONS,
- message: `Removed Charging Station EVSE UID '${evse.uid}' in Location '${location.name}' with ID '${location.id}'`,
- module: MODULE_NAME, method: 'processLocation',
- detailedMessages: { evse, location }
- });
- }
- continue;
- }
- // Update Charging Station
- const chargingStation = OCPIUtilsService.convertEvseToChargingStation(evse, location);
- chargingStation.companyID = siteArea.site?.companyID;
- chargingStation.siteID = siteArea.siteID;
- chargingStation.siteAreaID = siteArea.id;
- await ChargingStationStorage.saveChargingStation(this.tenant, chargingStation);
- await ChargingStationStorage.saveChargingStationOcpiData(this.tenant, chargingStation.id, chargingStation.ocpiData);
- await Logging.logDebug({
- tenantID: this.tenant.id,
- action: ServerAction.OCPI_PULL_LOCATIONS,
- message: `Updated Charging Station ID '${evse.evse_id}' in Location '${location.name}'`,
- module: MODULE_NAME, method: 'processLocation',
- detailedMessages: location
- });
- }
- }
- }
-
public async pushToken(token: OCPIToken): Promise<boolean> {
// Get tokens endpoint url
const tokensUrl = this.getEndpointUrl('tokens', ServerAction.OCPI_PUSH_TOKENS);

MODIFY
src/server/ocpi/OCPIUtils.ts
src/server/ocpi/OCPIUtils.ts
@@ -2,15 +2,31 @@ import ChargingStation, { Connector } from '../../types/ChargingStation';
import { OCPIToken, OCPITokenType } from '../../types/ocpi/OCPIToken';
import AppError from '../../exception/AppError';
+import BackendError from '../../exception/BackendError';
+import ChargingStationStorage from '../../storage/mongodb/ChargingStationStorage';
+import Company from '../../types/Company';
+import CompanyStorage from '../../storage/mongodb/CompanyStorage';
import Constants from '../../utils/Constants';
+import Logging from '../../utils/Logging';
+import OCPIEndpoint from '../../types/ocpi/OCPIEndpoint';
+import { OCPIEvseStatus } from '../../types/ocpi/OCPIEvse';
+import { OCPILocation } from '../../types/ocpi/OCPILocation';
import { OCPIResponse } from '../../types/ocpi/OCPIResponse';
import { OCPIStatusCode } from '../../types/ocpi/OCPIStatusCode';
+import OCPIUtilsService from './ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService';
import { Request } from 'express';
+import { ServerAction } from '../../types/Server';
+import Site from '../../types/Site';
+import SiteArea from '../../types/SiteArea';
+import SiteAreaStorage from '../../storage/mongodb/SiteAreaStorage';
+import SiteStorage from '../../storage/mongodb/SiteStorage';
+import Tenant from '../../types/Tenant';
import Utils from '../../utils/Utils';
import moment from 'moment';
-export default class OCPIUtils {
+const MODULE_NAME = 'OCPIUtils';
+export default class OCPIUtils {
public static getConnectorIDFromEvseID(evseID: string): string {
return evseID.split(Constants.OCPI_SEPARATOR).pop();
}
@@ -139,4 +155,124 @@ export default class OCPIUtils {
return authorizationDate && moment(authorizationDate).isAfter(moment().subtract(
Constants.ROAMING_AUTHORIZATION_TIMEOUT_MINS, 'minutes'));
}
+
+ public static async checkAndGetEMSPCompany(tenant: Tenant, ocpiEndpoint: OCPIEndpoint): Promise<Company> {
+ let company = await CompanyStorage.getCompany(tenant, ocpiEndpoint.id);
+ if (!company) {
+ company = {
+ id: ocpiEndpoint.id,
+ name: `${ocpiEndpoint.name} (${ocpiEndpoint.role})`,
+ issuer: false,
+ createdOn: new Date()
+ } as Company;
+ await CompanyStorage.saveCompany(tenant, company, false);
+ }
+ return company;
+ }
+
+ public static async processEMSPLocation(tenant: Tenant, location: OCPILocation, company: Company, existingSites: Site[],
+ countryCode?: string, partyID?: string): Promise<void> {
+ // Handle Site
+ let site: Site;
+ const siteName = location.operator?.name ?? OCPIUtils.buildOperatorName(countryCode, partyID);
+ site = existingSites.find((existingSite) => existingSite.name === siteName);
+ if (!site) {
+ // Create Site
+ site = {
+ name: siteName,
+ createdOn: new Date(),
+ companyID: company.id,
+ issuer: false,
+ address: {
+ address1: location.address,
+ postalCode: location.postal_code,
+ city: location.city,
+ country: location.country,
+ coordinates: []
+ }
+ } as Site;
+ if (location.coordinates?.latitude && location.coordinates?.longitude) {
+ site.address.coordinates = [
+ Utils.convertToFloat(location.coordinates.longitude),
+ Utils.convertToFloat(location.coordinates.latitude)
+ ];
+ }
+ site.id = await SiteStorage.saveSite(tenant, site, false);
+ // Push the Site then it can be retrieve in the next round
+ existingSites.push(site);
+ }
+ const locationName = site.name + Constants.OCPI_SEPARATOR + location.id;
+ // Handle Site Area
+ const siteAreas = await SiteAreaStorage.getSiteAreas(tenant,
+ { siteIDs: [site.id], name: locationName, issuer: false, withSite: true },
+ Constants.DB_PARAMS_SINGLE_RECORD);
+ let siteArea = !Utils.isEmptyArray(siteAreas.result) ? siteAreas.result[0] : null;
+ if (!siteArea) {
+ // Create Site Area
+ siteArea = {
+ name: locationName,
+ createdOn: new Date(),
+ siteID: site.id,
+ issuer: false,
+ address: {
+ address1: location.address,
+ address2: location.name,
+ postalCode: location.postal_code,
+ city: location.city,
+ country: location.country,
+ coordinates: []
+ }
+ } as SiteArea;
+ if (location.coordinates?.latitude && location.coordinates?.longitude) {
+ siteArea.address.coordinates = [
+ Utils.convertToFloat(location.coordinates.longitude),
+ Utils.convertToFloat(location.coordinates.latitude)
+ ];
+ }
+ siteArea.id = await SiteAreaStorage.saveSiteArea(tenant, siteArea, false);
+ }
+ if (!Utils.isEmptyArray(location.evses)) {
+ for (const evse of location.evses) {
+ if (!evse.uid) {
+ throw new BackendError({
+ action: ServerAction.OCPI_PULL_LOCATIONS,
+ message: `Missing Charging Station EVSE UID in Location '${location.name}' with ID '${location.id}'`,
+ module: MODULE_NAME, method: 'processLocation',
+ detailedMessages: { evse, location }
+ });
+ }
+ if (evse.status === OCPIEvseStatus.REMOVED) {
+ // Get existing charging station
+ const currentChargingStation = await ChargingStationStorage.getChargingStationByOcpiLocationUid(
+ tenant, location.id, evse.uid, ['id']
+ );
+ if (currentChargingStation) {
+ await ChargingStationStorage.deleteChargingStation(tenant, currentChargingStation.id);
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ action: ServerAction.OCPI_PULL_LOCATIONS,
+ message: `Removed Charging Station EVSE UID '${evse.uid}' in Location '${location.name}' with ID '${location.id}'`,
+ module: MODULE_NAME, method: 'processLocation',
+ detailedMessages: { evse, location }
+ });
+ }
+ continue;
+ }
+ // Update Charging Station
+ const chargingStation = OCPIUtilsService.convertEvseToChargingStation(evse, location);
+ chargingStation.companyID = siteArea.site?.companyID;
+ chargingStation.siteID = siteArea.siteID;
+ chargingStation.siteAreaID = siteArea.id;
+ await ChargingStationStorage.saveChargingStation(tenant, chargingStation);
+ await ChargingStationStorage.saveChargingStationOcpiData(tenant, chargingStation.id, chargingStation.ocpiData);
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ action: ServerAction.OCPI_PULL_LOCATIONS,
+ message: `Updated Charging Station ID '${evse.evse_id}' in Location '${location.name}'`,
+ module: MODULE_NAME, method: 'processLocation',
+ detailedMessages: location
+ });
+ }
+ }
+ }
}

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts
@@ -36,6 +36,7 @@ import { ServerAction } from '../../../../types/Server';
import SettingStorage from '../../../../storage/mongodb/SettingStorage';
import { SimplePricingSetting } from '../../../../types/Setting';
import Site from '../../../../types/Site';
+import SiteArea from '../../../../types/SiteArea';
import SiteStorage from '../../../../storage/mongodb/SiteStorage';
import { StatusCodes } from 'http-status-codes';
import Tag from '../../../../types/Tag';
@@ -122,7 +123,7 @@ export default class OCPIUtilsService {
// Convert Sites to Locations
for (const site of sites.result) {
ocpiLocationsResult.result.push(
- await OCPIUtilsService.convertSite2Location(tenant, site, options, withChargingStations));
+ await OCPIUtilsService.convertCPOSite2Location(tenant, site, options, withChargingStations));
}
let nbrOfSites = sites.count;
if (nbrOfSites === -1) {
@@ -171,7 +172,7 @@ export default class OCPIUtilsService {
};
}
- public static async convertSite2Location(tenant: Tenant, site: Site,
+ public static async convertCPOSite2Location(tenant: Tenant, site: Site,
options: OCPILocationOptions, withChargingStations: boolean): Promise<OCPILocation> {
// Build object
return {
@@ -194,6 +195,25 @@ export default class OCPIUtilsService {
};
}
+ public static async convertEMSPSiteArea2Location(tenant: Tenant, siteArea: SiteArea): Promise<OCPILocation> {
+ // Becareful: Not the full Location can be rebuilt
+ return {
+ id: siteArea.id.split('*')[2],
+ type: OCPILocationType.UNKNOWN,
+ name: siteArea.name,
+ address: siteArea.address?.address1,
+ city: siteArea.address?.city,
+ postal_code: siteArea.address?.postalCode,
+ country: siteArea.address?.country,
+ coordinates: {
+ longitude: siteArea.address?.coordinates[0].toString(),
+ latitude: siteArea.address?.coordinates[1].toString()
+ },
+ operator: await OCPIUtilsService.getOperatorBusinessDetails(tenant) ?? { name: 'Undefined' },
+ last_updated: siteArea.lastChangedOn ? siteArea.lastChangedOn : siteArea.createdOn,
+ };
+ }
+
// TODO: Implement the Opening Hours in the Site and send it to OCPI
public static buildOpeningTimes(tenant: Tenant, site: Site): OCPIOpeningTimes {
switch (tenant?.id) {
@@ -821,7 +841,7 @@ export default class OCPIUtilsService {
} else {
connectors = chargingStation.connectors.filter((connector) => connector !== null);
}
- const evses = connectors.map((connector) => {
+ return connectors.map((connector) => {
const evse: OCPIEvse = {
uid: OCPIUtils.buildEvseUID(chargingStation, connector),
evse_id: RoamingUtils.buildEvseID(options.countryID, options.partyID,
@@ -845,8 +865,6 @@ export default class OCPIUtilsService {
}
return evse;
});
- // Return all evses
- return evses;
}
private static convertChargingStation2UniqueEvse(tenant: Tenant, chargingStation: ChargingStation,

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOLocationsEndpoint.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOLocationsEndpoint.ts
@@ -121,7 +121,7 @@ export default class CPOLocationsEndpoint extends AbstractEndpoint {
// Get site
const site = await SiteStorage.getSite(tenant, locationId);
if (site) {
- return await OCPIUtilsService.convertSite2Location(tenant, site, options, true);
+ return await OCPIUtilsService.convertCPOSite2Location(tenant, site, options, true);
}
}

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPLocationsEndpoint.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPLocationsEndpoint.ts
@@ -10,7 +10,7 @@ import ChargingStationStorage from '../../../../../storage/mongodb/ChargingStati
import Constants from '../../../../../utils/Constants';
import { HTTPError } from '../../../../../types/HTTPError';
import Logging from '../../../../../utils/Logging';
-import OCPIClientFactory from '../../../../../client/ocpi/OCPIClientFactory';
+import LoggingHelper from '../../../../../utils/LoggingHelper';
import { OCPIConnector } from '../../../../../types/ocpi/OCPIConnector';
import OCPIEndpoint from '../../../../../types/ocpi/OCPIEndpoint';
import { OCPILocation } from '../../../../../types/ocpi/OCPILocation';
@@ -59,17 +59,7 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint {
ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR
});
}
- const location = req.body as OCPILocation;
- if (locationID !== location.id) {
- throw new AppError({
- action: ServerAction.OCPI_PATCH_LOCATION,
- module: MODULE_NAME, method: 'patchLocationRequest',
- errorCode: StatusCodes.NOT_FOUND,
- message: `Location ID '${locationID}' mismatch in URL`,
- ocpiError: OCPIStatusCode.CODE_2003_UNKNOWN_LOCATION_ERROR,
- detailedMessages: { locationId: locationID, location }
- });
- }
+ // Handle Charging Station / Connector
if (evseUID) {
const chargingStation = await ChargingStationStorage.getChargingStationByOcpiLocationUid(
tenant, locationID, evseUID);
@@ -80,20 +70,23 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint {
errorCode: StatusCodes.NOT_FOUND,
message: `Unknown Charging Station with EVSE UID '${evseUID}' and Location ID '${locationID}'`,
ocpiError: OCPIStatusCode.CODE_2003_UNKNOWN_LOCATION_ERROR,
- detailedMessages: { locationId: locationID, evseUid: evseUID, location }
+ detailedMessages: { countryCode, partyID, locationID, evseUID }
});
}
- const foundEvse = location.evses.find((evse) => evse.uid === evseUID);
- if (!foundEvse) {
+ if (!chargingStation.siteArea) {
throw new AppError({
+ ...LoggingHelper.getChargingStationProperties(chargingStation),
action: ServerAction.OCPI_PATCH_LOCATION,
module: MODULE_NAME, method: 'patchLocationRequest',
errorCode: StatusCodes.NOT_FOUND,
- message: `EVSE UID mismatch '${evseUID}' in URL`,
+ message: `Site Area ID '${chargingStation.siteAreaID}' does not exists`,
ocpiError: OCPIStatusCode.CODE_2003_UNKNOWN_LOCATION_ERROR,
- detailedMessages: { evseUid: evseUID, location }
+ detailedMessages: { countryCode, partyID, locationID, evseUID, chargingStation }
});
}
+ // Rebuild Location
+ const location = await OCPIUtilsService.convertEMSPSiteArea2Location(tenant, chargingStation.siteArea);
+ // Get the EVSE
const chargingStationEvse = chargingStation.ocpiData.evses.find((evse) => evse.uid === evseUID);
if (!chargingStationEvse) {
throw new AppError({
@@ -102,35 +95,32 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint {
errorCode: StatusCodes.NOT_FOUND,
message: `Unknown EVSE UID '${evseUID}'`,
ocpiError: OCPIStatusCode.CODE_2003_UNKNOWN_LOCATION_ERROR,
- detailedMessages: { evseUid: evseUID, chargingStation, location }
+ detailedMessages: { countryCode, partyID, locationID, evseUID, location }
});
}
if (connectorID) {
- const foundEvseConnector = foundEvse.connectors.find((evseConnector) => evseConnector.id === connectorID);
- if (!foundEvseConnector) {
- throw new AppError({
- action: ServerAction.OCPI_PATCH_LOCATION,
- module: MODULE_NAME, method: 'patchLocationRequest',
- errorCode: StatusCodes.NOT_FOUND,
- message: `Unknown Connector ID '${connectorID}' in EVSE UID '${evseUID}'`,
- ocpiError: OCPIStatusCode.CODE_2003_UNKNOWN_LOCATION_ERROR,
- detailedMessages: { connectorId: connectorID, evse: foundEvse, location }
- });
- }
- // Patch Connector
- await this.patchEvseConnector(tenant, chargingStation, foundEvseConnector);
+ const evseConnector = req.body as OCPIConnector;
+ // const foundEvseConnector = foundEvse.connectors.find((evseConnector) => evseConnector.id === connectorID);
+ // if (!foundEvseConnector) {
+ // throw new AppError({
+ // action: ServerAction.OCPI_PATCH_LOCATION,
+ // module: MODULE_NAME, method: 'patchLocationRequest',
+ // errorCode: StatusCodes.NOT_FOUND,
+ // message: `Unknown Connector ID '${connectorID}' in EVSE UID '${evseUID}'`,
+ // ocpiError: OCPIStatusCode.CODE_2003_UNKNOWN_LOCATION_ERROR,
+ // detailedMessages: { connectorId: connectorID, evse: foundEvse, location }
+ // });
+ // }
+ // // Patch Connector
+ // await this.patchEvseConnector(tenant, chargingStation, foundEvseConnector);
} else {
+ const evse = req.body as OCPIEvse;
// Patch Evse
- await this.patchEvse(tenant, chargingStation, foundEvse, location);
+ await this.patchEvse(tenant, chargingStation, chargingStationEvse, location);
}
+ // Handle Location
} else {
- await Logging.logError({
- tenantID: tenant.id,
- action: ServerAction.OCPI_PATCH_LOCATION,
- message: `Patching of Location ID '${locationID}' is not supported currently`,
- module: MODULE_NAME, method: 'patchLocationRequest',
- detailedMessages: { location }
- });
+ const location = req.body as OCPILocation;
}
return OCPIUtils.success();
}
@@ -152,7 +142,6 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint {
ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR
});
}
- const ocpiClient = await OCPIClientFactory.getEmspOcpiClient(tenant, ocpiEndpoint);
const location = req.body as OCPILocation;
if (locationID !== location.id) {
throw new AppError({
@@ -198,10 +187,10 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint {
}
} else {
// Update Location
- const company = await ocpiClient.checkAndGetCompany();
+ const company = await OCPIUtils.checkAndGetEMSPCompany(tenant, ocpiEndpoint);
const siteName = OCPIUtils.buildOperatorName(countryCode, partyID);
const sites = await SiteStorage.getSites(tenant, { companyIDs: [company.id], name: siteName }, Constants.DB_PARAMS_SINGLE_RECORD);
- await ocpiClient.processLocation(location, company, sites.result);
+ await OCPIUtils.processEMSPLocation(tenant, location, company, sites.result, countryCode, partyID);
}
return OCPIUtils.success();
}

MODIFY
src/types/ocpi/OCPIConnector.ts
src/types/ocpi/OCPIConnector.ts
@@ -10,6 +10,7 @@ export interface OCPIConnector {
voltage: OCPIVoltage;
amperage: number;
tariff_id?: string;
+ terms_and_conditions?: string;
last_updated: Date;
}

MODIFY
src/types/ocpi/OCPIEvse.ts
src/types/ocpi/OCPIEvse.ts
+import { OCPICoordinates, OCPIImage } from './OCPILocation';
+
import { OCPIConnector } from './OCPIConnector';
+import { OCPIDisplayText } from './OCPIDisplayText';
export interface OCPIEvse {
uid: string;
- evse_id: string;
- location_id: string;
+ evse_id?: string;
status: OCPIEvseStatus;
- capabilities: OCPICapability[];
+ status_schedule?: OCPIStatusSchedule[];
+ capabilities?: OCPICapability[];
connectors: OCPIConnector[];
- coordinates: {
- latitude: string;
- longitude: string;
- };
+ floor_level?: string;
+ coordinates?: OCPICoordinates;
+ physical_reference?: string;
+ directions?: OCPIDisplayText[];
+ parking_restrictions?: OCPIParkingRestriction[];
+ images?: OCPIImage[];
last_updated: Date;
+ location_id: string;
chargingStationID?: string;
siteID?: string;
siteAreaID?: string;
companyID?: string;
}
+export enum OCPIParkingRestriction {
+ EV_ONLY = 'EV_ONLY',
+ PLUGGED = 'PLUGGED',
+ DISABLED = 'DISABLED',
+ CUSTOMERS = 'CUSTOMERS',
+ MOTORCYCLES = 'MOTORCYCLES',
+}
+
+export interface OCPIStatusSchedule {
+ period_begin: Date;
+ period_end?: Date;
+ status: OCPIEvseStatus;
+}
+
export enum OCPIEvseStatus {
AVAILABLE = 'AVAILABLE',
BLOCKED = 'BLOCKED',

MODIFY
src/types/ocpi/OCPILocation.ts
src/types/ocpi/OCPILocation.ts
@@ -25,19 +25,96 @@ export interface OCPILocation {
city: string;
postal_code: string;
country: string;
- coordinates: {
- latitude: string;
- longitude: string;
- };
+ coordinates: OCPICoordinates;
+ related_locations?: OCPICoordinates[];
+ evses?: OCPIEvse[];
+ directions?: OCPIDisplayText[];
operator?: OCPIBusinessDetails;
suboperator?: OCPIBusinessDetails;
owner?: OCPIBusinessDetails;
- evses: OCPIEvse[];
- directions?: OCPIDisplayText[];
- last_updated: Date;
- // OCPI specify that attribute as optional but Gireve requires it
- opening_times: OCPIOpeningTimes;
+ facilities?: OCPIFacilities[];
+ time_zone?: string;
+ opening_times?: OCPIOpeningTimes;
charging_when_closed?: boolean;
+ images?: OCPIImage[];
+ energy_mix?: OCPIEnergyMix;
+ last_updated: Date;
+}
+
+export interface OCPIEnergyMix {
+ is_green_energy: boolean;
+ energy_sources?: OCPIEnergySource[];
+ environ_impact?: OCPIEnvironmentalImpact[];
+ supplier_name?: string;
+ energy_product_name: string;
+}
+
+export interface OCPIEnergySource {
+ source: OCPIEnergySourceCategory;
+ percentage: number;
+}
+
+export enum OCPIEnergySourceCategory {
+ NUCLEAR = 'NUCLEAR',
+ GENERAL_FOSSIL = 'GENERAL_FOSSIL',
+ COAL = 'COAL',
+ GAS = 'GAS',
+ GENERAL_GREEN = 'GENERAL_GREEN',
+ SOLAR = 'SOLAR',
+ WIND = 'WIND',
+ WATER = 'WATER'
+}
+
+export interface OCPIEnvironmentalImpact {
+ source: OCPIEnvironmentalImpactCategory;
+ amount: number;
+}
+
+export enum OCPIEnvironmentalImpactCategory {
+ NUCLEAR_WASTE = 'NUCLEAR_WASTE',
+ CARBON_DIOXIDE = 'CARBON_DIOXIDE'
+}
+export interface OCPICoordinates {
+ latitude: string;
+ longitude: string;
+}
+
+export enum OCPIFacilities {
+ HOTEL = 'HOTEL',
+ RESTAURANT = 'RESTAURANT',
+ CAFE = 'CAFE',
+ MALL = 'MALL',
+ SUPERMARKET = 'SUPERMARKET',
+ SPORT = 'SPORT',
+ RECREATION_AREA = 'RECREATION_AREA',
+ NATURE = 'NATURE',
+ MUSEUM = 'MUSEUM',
+ BUS_STOP = 'BUS_STOP',
+ TAXI_STAND = 'TAXI_STAND',
+ TRAIN_STATION = 'TRAIN_STATION',
+ AIRPORT = 'AIRPORT',
+ CARPOOL_PARKING = 'CARPOOL_PARKING',
+ FUEL_STATION = 'FUEL_STATION',
+ WIFI = 'WIFI'
+}
+
+export interface OCPIImage {
+ url: string;
+ thumbnail?: string;
+ category: OCPIImageCategory;
+ type: string;
+ width?: number;
+ height?: number;
+}
+
+export enum OCPIImageCategory {
+ CHARGER = 'CHARGER',
+ ENTRANCE = 'ENTRANCE',
+ LOCATION = 'LOCATION',
+ NETWORK = 'NETWORK',
+ OPERATOR = 'OPERATOR',
+ OTHER = 'OTHER',
+ OWNER = 'OWNER'
}
export interface OCPIOpeningTimes {

Finish the job 1/2