Greencom integration
MODIFY
src/integration/asset/AssetFactory.ts
src/integration/asset/AssetFactory.ts
import { AssetConnectionType, AssetSetting } from '../../types/Setting';
import AssetIntegration from './AssetIntegration';
+import GreencomAssetIntegration from './greencom/GreencomAssetIntegration';
import Logging from '../../utils/Logging';
import SchneiderAssetIntegration from './schneider/SchneiderAssetIntegration';
import { ServerAction } from '../../types/Server';
@@ -29,6 +30,9 @@ export default class AssetFactory {
case AssetConnectionType.SCHNEIDER:
assetIntegrationImpl = new SchneiderAssetIntegration(tenantID, settings.asset, foundConnection);
break;
+ case AssetConnectionType.GREENCOM:
+ assetIntegrationImpl = new GreencomAssetIntegration(tenantID, settings.asset, foundConnection);
+ break;
}
return assetIntegrationImpl;
}

ADD
src/integration/asset/greencom/GreencomAssetIntegration.ts
+import Asset, { AssetType } from '../../../types/Asset';
+import { AssetConnectionSetting, AssetSetting } from '../../../types/Setting';
+
+import { AbstractCurrentConsumption } from '../../../types/Consumption';
+import AssetIntegration from '../AssetIntegration';
+import AxiosFactory from '../../../utils/AxiosFactory';
+import { AxiosInstance } from 'axios';
+import BackendError from '../../../exception/BackendError';
+import Constants from '../../../utils/Constants';
+import Cypher from '../../../utils/Cypher';
+import Logging from '../../../utils/Logging';
+import { ServerAction } from '../../../types/Server';
+import Utils from '../../../utils/Utils';
+import moment from 'moment';
+
+const MODULE_NAME = 'GreencomAssetIntegration';
+
+export default class GreencomAssetIntegration extends AssetIntegration<AssetSetting> {
+ private axiosInstance: AxiosInstance;
+
+ public constructor(tenantID: string, settings: AssetSetting, connection: AssetConnectionSetting) {
+ super(tenantID, settings, connection);
+ this.axiosInstance = AxiosFactory.getAxiosInstance(tenantID);
+ }
+
+ public async checkConnection(): Promise<void> {
+ await this.connect();
+ }
+
+ public async retrieveConsumption(asset: Asset): Promise<AbstractCurrentConsumption> {
+ const currentTime = moment();
+
+ // Set new Token
+ const token = await this.connect();
+ const request = `${this.connection.url}/site-api/${asset.meterID}?withEnergy=true&withPower=true&from=${currentTime.subtract(1, 'minutes').toISOString()}&to=${currentTime.add(1, 'minutes').toISOString()}&step=PT1M`;
+ try {
+ // Get consumption
+ const response = await this.axiosInstance.get(
+ request,
+ {
+ headers: this.buildAuthHeader(token)
+ }
+ );
+ Logging.logDebug({
+ tenantID: this.tenantID,
+ source: Constants.CENTRAL_SERVER,
+ action: ServerAction.RETRIEVE_ASSET_CONSUMPTION,
+ message: `${asset.name} > GreenCom web service has been called successfully`,
+ module: MODULE_NAME, method: 'retrieveConsumption',
+ detailedMessages: { response: response.data }
+ });
+ return this.filterConsumptionRequest(asset, response.data, new Date(currentTime.toDate()));
+ } catch (error) {
+ throw new BackendError({
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME,
+ method: 'retrieveConsumption',
+ action: ServerAction.RETRIEVE_ASSET_CONSUMPTION,
+ message: 'Error while retrieving the asset consumption',
+ detailedMessages: { request, token, error: error.message, stack: error.stack, asset }
+ });
+ }
+ return null;
+ }
+
+ private filterConsumptionRequest(asset: Asset, data: any, currentTime: Date): AbstractCurrentConsumption {
+ const consumption = {} as AbstractCurrentConsumption;
+ // Convert data value to number and get consumption
+ let consumptionWh = 0;
+
+ switch (asset.assetType) {
+ case AssetType.CO:
+ consumption.currentInstantWatts = data.power.average;
+ consumption.currentInstantAmps = consumption.currentInstantWatts / asset.siteArea.voltage;
+ consumptionWh = data.energy.sum;
+ break;
+ case AssetType.PR:
+ consumption.currentInstantWatts = data.power.average * -1;
+ consumption.currentInstantAmps = consumption.currentInstantWatts / asset.siteArea.voltage;
+ consumptionWh = data.energy.sum * -1;
+ break;
+ case AssetType.CO_PR:
+ consumption.currentInstantWatts = data.power.charge.average - data.power.discharge.average;
+ consumption.currentInstantAmps = consumption.currentInstantWatts / asset.siteArea.voltage;
+ consumptionWh = data.energy.charge.sum - data.energy.discharge.sum;
+ break;
+ }
+
+ consumption.lastConsumption = {
+ value: consumptionWh,
+ timestamp: currentTime,
+ };
+
+ return consumption;
+ }
+
+
+ private async connect(): Promise<string> {
+ // Check if connection is initialized
+ this.checkConnectionIsProvided();
+ // Get credential params
+ const credentials = this.getAuthentication();
+ // Send credentials to get the token
+ const response = await Utils.executePromiseWithTimeout(5000,
+ this.axiosInstance.post(`${this.connection.url}/authentication-api/tokens`,
+ credentials,
+ {
+ // @ts-ignore
+ 'axios-retry': {
+ retries: 0
+ },
+ headers: this.buildFormHeaders()
+ }),
+ `Time out error (5s) when getting the token with the connection URL '${this.connection.url}/authentication-api/tokens'`
+ );
+ // Return the Token
+ return response.data.access_token;
+ }
+
+ private getAuthentication(): any {
+ return {
+ 'grant_type': 'client_credentials',
+ 'client_id': this.connection.greencomConnection.clientId,
+ 'client_secret': Cypher.decrypt(this.connection.greencomConnection.clientSecret)
+ };
+ }
+
+ private checkConnectionIsProvided(): void {
+ if (!this.connection) {
+ throw new BackendError({
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME,
+ method: 'checkConnectionIsProvided',
+ action: ServerAction.CHECK_CONNECTION,
+ message: 'No connection provided'
+ });
+ }
+ }
+
+ private buildFormHeaders(): any {
+ return {
+ 'Content-Type': 'application/json'
+ };
+ }
+
+ private buildAuthHeader(token: string): any {
+ return {
+ 'Authorization': 'Bearer ' + token
+ };
+ }
+}

MODIFY
src/integration/asset/schneider/SchneiderAssetIntegration.ts
src/integration/asset/schneider/SchneiderAssetIntegration.ts
@@ -126,8 +126,8 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
private getCredentialURLParams(): URLSearchParams {
const params = new URLSearchParams();
params.append('grant_type', 'password');
- params.append('username', this.connection.connection.user);
- params.append('password', Cypher.decrypt(this.connection.connection.password));
+ params.append('username', this.connection.schneiderConnection.user);
+ params.append('password', Cypher.decrypt(this.connection.schneiderConnection.password));
return params;
}

MODIFY
src/server/rest/v1/service/security/SettingSecurity.ts
src/server/rest/v1/service/security/SettingSecurity.ts
@@ -169,17 +169,19 @@ export default class SettingSecurity {
url: sanitize(connection.url),
type: sanitize(connection.type),
timestamp: new Date(),
- connection: {
- user: sanitize(connection.connection.user),
- password: sanitize(connection.connection.password)
- }
};
// Check type
switch (connection.type) {
case AssetConnectionType.SCHNEIDER:
- sanitizedConnection.connection = {
- user: sanitize(connection.connection.user),
- password: sanitize(connection.connection.password),
+ sanitizedConnection.schneiderConnection = {
+ user: sanitize(connection.schneiderConnection.user),
+ password: sanitize(connection.schneiderConnection.password),
+ };
+ break;
+ case AssetConnectionType.GREENCOM:
+ sanitizedConnection.greencomConnection = {
+ clientId: sanitize(connection.greencomConnection.clientId),
+ clientSecret: sanitize(connection.greencomConnection.clientSecret),
};
break;
}

MODIFY
src/types/Setting.ts
src/types/Setting.ts
@@ -223,11 +223,13 @@ export interface AssetConnectionSetting {
url: string;
timestamp: Date;
type: AssetConnectionType;
- connection?: AssetSchneiderConnectionType;
+ schneiderConnection?: AssetSchneiderConnectionType;
+ greencomConnection?: AssetGreencomConnectionType;
}
export enum AssetConnectionType {
SCHNEIDER = 'schneider',
+ GREENCOM = 'greencom'
}
export interface AssetUserPasswordConnectionType {
@@ -238,3 +240,8 @@ export interface AssetUserPasswordConnectionType {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface AssetSchneiderConnectionType extends AssetUserPasswordConnectionType {
}
+export interface AssetGreencomConnectionType {
+ clientId: string;
+ clientSecret: string;
+}
+

Greencom integration