update EVSE Status to Hubject
MODIFY
src/client/oicp/CpoOICPClient.ts
src/client/oicp/CpoOICPClient.ts
+import ChargingStation, { Connector } from '../../types/ChargingStation';
import { OICPActionType, OICPPushEvseDataCpoSend } from '../../types/oicp/OICPEvseData';
import { OICPAuthorizeStartCpoReceive, OICPAuthorizeStartCpoSend, OICPAuthorizeStopCpoReceive, OICPAuthorizeStopCpoSend } from '../../types/oicp/OICPAuthorize';
import { OICPChargingNotification, OICPCode, OICPErrorClass } from '../../types/oicp/OICPStatusCode';
@@ -10,7 +11,6 @@ import { OICPSession, OICPSessionStatus } from '../../types/oicp/OICPSession';
import Transaction, { TransactionAction } from '../../types/Transaction';
import BackendError from '../../exception/BackendError';
-import ChargingStation from '../../types/ChargingStation';
import Constants from '../../utils/Constants';
import { HTTPError } from '../../types/HTTPError';
import Logging from '../../utils/Logging';
@@ -484,6 +484,45 @@ export default class CpoOICPClient extends OICPClient {
return sendResult;
}
+ /**
+ * Update EVSE Status
+ */
+ async updateEVSEStatus(chargingStation: ChargingStation, connector: Connector): Promise<OICPAcknowledgment> {
+ if (!chargingStation.siteAreaID && !chargingStation.siteArea) {
+ throw new BackendError({
+ source: chargingStation.id,
+ action: ServerAction.OICP_UPDATE_EVSE_STATUS,
+ message: 'Charging Station must be associated to a site area',
+ module: MODULE_NAME, method: 'updateEVSEStatus',
+ });
+ }
+ if (!chargingStation.issuer) {
+ throw new BackendError({
+ source: chargingStation.id,
+ action: ServerAction.OICP_UPDATE_EVSE_STATUS,
+ message: 'Only charging Station issued locally can be exposed to Hubject',
+ module: MODULE_NAME, method: 'updateEVSEStatus',
+ });
+ }
+ if (!chargingStation.public) {
+ throw new BackendError({
+ source: chargingStation.id,
+ action: ServerAction.OICP_UPDATE_EVSE_STATUS,
+ message: 'Private charging Station cannot be exposed to Hubject',
+ module: MODULE_NAME, method: 'updateEVSEStatus',
+ });
+ }
+ // Define get option
+ const options = {
+ addChargeBoxID: true,
+ countryID: this.getLocalCountryCode(ServerAction.OICP_PUSH_EVSE_STATUSES),
+ partyID: this.getLocalPartyID(ServerAction.OICP_PUSH_EVSE_STATUSES)
+ };
+ const evseStatus = OICPMapping.convertConnector2EvseStatus(this.tenant, chargingStation, connector, options);
+ const response = await this.pushEvseStatus([evseStatus], OICPActionType.update);
+ return response;
+ }
+
/**
* Push EVSE
*/

MODIFY
src/server/ocpp/services/OCPPService.ts
src/server/ocpp/services/OCPPService.ts
@@ -12,6 +12,7 @@ import Configuration from '../../../utils/Configuration';
import Constants from '../../../utils/Constants';
import ConsumptionStorage from '../../../storage/mongodb/ConsumptionStorage';
import CpoOCPIClient from '../../../client/ocpi/CpoOCPIClient';
+import CpoOICPClient from '../../../client/oicp/CpoOICPClient';
import I18nManager from '../../../utils/I18nManager';
import LockingHelper from '../../../locking/LockingHelper';
import LockingManager from '../../../locking/LockingManager';
@@ -24,6 +25,8 @@ import { OCPPHeader } from '../../../types/ocpp/OCPPHeader';
import OCPPStorage from '../../../storage/mongodb/OCPPStorage';
import OCPPUtils from '../utils/OCPPUtils';
import OCPPValidation from '../validation/OCPPValidation';
+import OICPClientFactory from '../../../client/oicp/OICPClientFactory';
+import { OICPRole } from '../../../types/oicp/OICPRole';
import RegistrationToken from '../../../types/RegistrationToken';
import RegistrationTokenStorage from '../../../storage/mongodb/RegistrationTokenStorage';
import { ServerAction } from '../../../types/Server';
@@ -1002,6 +1005,8 @@ export default class OCPPService {
await this.notifyStatusNotification(tenantID, chargingStation, statusNotification);
// Send new status to IOP
await this.updateOCPIStatus(tenantID, chargingStation, foundConnector);
+ // Send new status to Hubject
+ await this.updateOICPStatus(tenantID, chargingStation, foundConnector);
// Save
await ChargingStationStorage.saveChargingStation(tenantID, chargingStation);
// Trigger Smart Charging
@@ -1103,6 +1108,27 @@ export default class OCPPService {
}
}
+ private async updateOICPStatus(tenantID: string, chargingStation: ChargingStation, connector: Connector) {
+ const tenant: Tenant = await TenantStorage.getTenant(tenantID);
+ if (chargingStation.issuer && chargingStation.public && Utils.isTenantComponentActive(tenant, TenantComponents.OICP)) {
+ try {
+ const oicpClient = await OICPClientFactory.getAvailableOicpClient(tenant, OICPRole.CPO) as CpoOICPClient;
+ if (oicpClient) {
+ await oicpClient.updateEVSEStatus(chargingStation, connector);
+ }
+ } catch (error) {
+ Logging.logError({
+ tenantID: tenantID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'updateOICPStatus',
+ action: ServerAction.OICP_UPDATE_EVSE_STATUS,
+ message: `An error occurred while updating the charging station status of ${chargingStation.id}`,
+ detailedMessages: { error: error.message, stack: error.stack }
+ });
+ }
+ }
+ }
+
private async notifyStatusNotification(tenantID: string, chargingStation: ChargingStation, statusNotification: OCPPStatusNotificationRequestExtended) {
// Faulted?
if (statusNotification.errorCode !== ChargePointErrorCode.NO_ERROR) {

MODIFY
src/server/oicp/oicp-services-impl/oicp-2.3.0/OICPMapping.ts
src/server/oicp/oicp-services-impl/oicp-2.3.0/OICPMapping.ts
@@ -136,6 +136,20 @@ export default class OICPMapping {
// Loop through connectors and send one evse per connector
const connectors = chargingStation.connectors.filter((connector) => connector !== null);
const evseStatuses = connectors.map((connector) => {
+ const evseStatus = OICPMapping.convertConnector2EvseStatus(tenant, chargingStation, connector, options);
+ return evseStatus;
+ });
+ // Return all EVSE Statuses
+ return evseStatuses;
+ }
+
+ /**
+ * Convert Connector to EVSE Status
+ * @param {Tenant} tenant
+ * @param {*} connector
+ * @return Array of OICP EVSE Statuses
+ */
+ static convertConnector2EvseStatus(tenant: Tenant, chargingStation: ChargingStation, connector: Connector, options: { countryID: string; partyID: string; addChargeBoxID?: boolean}): OICPEvseStatusRecord {
const evseID = OICPUtils.buildEvseID(options.countryID, options.partyID, chargingStation, connector);
const evseStatus: OICPEvseStatusRecord = {} as OICPEvseStatusRecord;
evseStatus.EvseID = evseID;
@@ -147,9 +161,6 @@ export default class OICPMapping {
}
return evseStatus;
- });
- // Return all EVSE Statuses
- return evseStatuses;
}
/**

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -152,6 +152,7 @@ export enum ServerAction {
OICP_ENPOINT_TRIGGER_JOBS = 'OicpEndpointTriggerJobs',
OICP_PUSH_EVSES = 'OicpPushEvses',
OICP_PUSH_EVSE_STATUSES = 'OicpPushEvseStatuses',
+ OICP_UPDATE_EVSE_STATUS = 'OicpUpdateEvseStatus',
OICP_AUTHORIZE_START = 'OicpAuthorizeStart',
OICP_AUTHORIZE_STOP = 'OicpAuthorizeStop',
OICP_PUSH_CDRS = 'OicpPushCdrs',

update EVSE Status to Hubject