Send notification once every 5 mins for admin
Ignore the Status Notif with same values
Log when a notification is not sent
Made Notification sync
MODIFY
src/authorization/Authorizations.ts
src/authorization/Authorizations.ts
@@ -785,7 +785,7 @@ export default class Authorizations {
return { user };
}
// Create the Tag as inactive and abort
- this.notifyUnknownBadgeHasBeenUsedAndAbort(action, tenant, tagID, chargingStation);
+ await this.notifyUnknownBadgeHasBeenUsedAndAbort(action, tenant, tagID, chargingStation);
}
// Get Authorized User
const user = await this.checkAndGetAuthorizedUserFromTag(action, tenant, chargingStation, transaction, tag, authAction);
@@ -973,7 +973,7 @@ export default class Authorizations {
return user;
}
- private static notifyUnknownBadgeHasBeenUsedAndAbort(
+ private static async notifyUnknownBadgeHasBeenUsedAndAbort(
action: ServerAction, tenant: Tenant, tagID: string, chargingStation: ChargingStation) {
const tag: Tag = {
id: tagID,
@@ -984,7 +984,7 @@ export default class Authorizations {
default: false
};
// Notify (Async)
- NotificationHandler.sendUnknownUserBadged(
+ await NotificationHandler.sendUnknownUserBadged(
tenant,
Utils.generateUUID(),
chargingStation,

MODIFY
src/client/ocpi/CpoOCPIClient.ts
src/client/ocpi/CpoOCPIClient.ts
@@ -747,7 +747,7 @@ export default class CpoOCPIClient extends OCPIClient {
if (result.failure > 0) {
// Send notification to admins
// eslint-disable-next-line @typescript-eslint/no-floating-promises
- NotificationHandler.sendOCPIPatchChargingStationsStatusesError(
+ await NotificationHandler.sendOCPIPatchChargingStationsStatusesError(
this.tenant,
{
location: location.name,

MODIFY
src/client/oicp/CpoOICPClient.ts
src/client/oicp/CpoOICPClient.ts
@@ -287,8 +287,7 @@ export default class CpoOICPClient extends OICPClient {
} while (!Utils.isEmptyArray(sites));
// Send notification to admins
if (result.failure > 0) {
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- NotificationHandler.sendOICPPatchChargingStationsError(
+ await NotificationHandler.sendOICPPatchChargingStationsError(
this.tenant,
{
evseDashboardURL: Utils.buildEvseURL(this.tenant.subdomain)
@@ -414,8 +413,7 @@ export default class CpoOICPClient extends OICPClient {
}
if (result.failure > 0) {
// Send notification to admins
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
- NotificationHandler.sendOICPPatchChargingStationsStatusesError(
+ await NotificationHandler.sendOICPPatchChargingStationsStatusesError(
this.tenant,
{
evseDashboardURL: Utils.buildEvseURL(this.tenant.subdomain)

MODIFY
src/notification/NotificationHandler.ts
src/notification/NotificationHandler.ts
@@ -123,6 +123,19 @@ export default class NotificationHandler {
// Send
await notificationSource.notificationTask.sendEndOfCharge(sourceData, user, tenant, NotificationSeverity.INFO);
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendEndOfCharge',
+ action: ServerAction.END_OF_CHARGE,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.END_OF_CHARGE, error);
@@ -165,6 +178,19 @@ export default class NotificationHandler {
// Send
await notificationSource.notificationTask.sendOptimalChargeReached(sourceData, user, tenant, NotificationSeverity.INFO);
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendOptimalChargeReached',
+ action: ServerAction.OPTIMAL_CHARGE_REACHED,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.OPTIMAL_CHARGE_REACHED, error);
@@ -207,6 +233,19 @@ export default class NotificationHandler {
// Send
await notificationSource.notificationTask.sendEndOfSession(sourceData, user, tenant, NotificationSeverity.INFO);
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendEndOfSession',
+ action: ServerAction.END_OF_SESSION,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.END_OF_SESSION, error);
@@ -249,6 +288,19 @@ export default class NotificationHandler {
// Send
await notificationSource.notificationTask.sendEndOfSignedSession(sourceData, user, tenant, NotificationSeverity.INFO);
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendEndOfSignedSession',
+ action: ServerAction.END_OF_SESSION,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.END_OF_SESSION, error);
@@ -429,12 +481,28 @@ export default class NotificationHandler {
// Active?
if (notificationSource.enabled) {
try {
+ // Check notification
+ const hasBeenNotified = await NotificationHandler.hasNotifiedSource(
+ tenant, notificationSource.channel, ServerAction.VERIFICATION_EMAIL,
+ null, user.id, { intervalMins: 10 });
+ // Notified?
+ if (!hasBeenNotified) {
// Save
await NotificationHandler.saveNotification(
tenant, notificationSource.channel, notificationID, ServerAction.VERIFICATION_EMAIL, { user });
// Send
await notificationSource.notificationTask.sendVerificationEmail(
sourceData, user, tenant, NotificationSeverity.INFO);
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendVerificationEmail',
+ action: ServerAction.VERIFICATION_EMAIL,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
+ }
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.VERIFICATION_EMAIL, error);
}
@@ -485,6 +553,12 @@ export default class NotificationHandler {
// Active?
if (notificationSource.enabled) {
try {
+ // Check notification
+ const hasBeenNotified = await NotificationHandler.hasNotifiedSource(
+ tenant, notificationSource.channel, ServerAction.CHARGING_STATION_STATUS_ERROR,
+ sourceData.chargeBoxID, null, { intervalMins: 10 });
+ // Notified?
+ if (!hasBeenNotified) {
// Save
await NotificationHandler.saveNotification(
tenant, notificationSource.channel, notificationID, ServerAction.CHARGING_STATION_STATUS_ERROR, {
@@ -500,6 +574,19 @@ export default class NotificationHandler {
await notificationSource.notificationTask.sendChargingStationStatusError(
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendChargingStationStatusError',
+ action: ServerAction.CHARGING_STATION_STATUS_ERROR,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
+ }
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.CHARGING_STATION_STATUS_ERROR, error);
}
@@ -612,6 +699,19 @@ export default class NotificationHandler {
await notificationSource.notificationTask.sendSessionStarted(
sourceData, user, tenant, NotificationSeverity.INFO);
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendSessionStarted',
+ action: ServerAction.TRANSACTION_STARTED,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.TRANSACTION_STARTED, error);
@@ -653,6 +753,14 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendSmtpError',
+ action: ServerAction.EMAIL_SERVER_ERROR,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.EMAIL_SERVER_ERROR, error);
@@ -701,6 +809,14 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendOCPIPatchChargingStationsStatusesError',
+ action: ServerAction.PATCH_EVSE_STATUS_ERROR,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.PATCH_EVSE_STATUS_ERROR, error);
@@ -744,6 +860,14 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendOICPPatchChargingStationsStatusesError',
+ action: ServerAction.PATCH_EVSE_STATUS_ERROR,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.PATCH_EVSE_STATUS_ERROR, error);
@@ -787,6 +911,14 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendOICPPatchChargingStationsError',
+ action: ServerAction.PATCH_EVSE_ERROR,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.PATCH_EVSE_ERROR, error);
@@ -821,6 +953,15 @@ export default class NotificationHandler {
// Send
await notificationSource.notificationTask.sendUserAccountInactivity(
sourceData, user, tenant, NotificationSeverity.INFO);
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendUserAccountInactivity',
+ action: ServerAction.USER_ACCOUNT_INACTIVITY,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.USER_ACCOUNT_INACTIVITY, error);
@@ -863,6 +1004,19 @@ export default class NotificationHandler {
// Send
await notificationSource.notificationTask.sendPreparingSessionNotStarted(sourceData, user, tenant, NotificationSeverity.INFO);
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendPreparingSessionNotStarted',
+ action: ServerAction.PREPARING_SESSION_NOT_STARTED,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.PREPARING_SESSION_NOT_STARTED, error);
@@ -905,6 +1059,14 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.INFO);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendOfflineChargingStations',
+ action: ServerAction.OFFLINE_CHARGING_STATIONS,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.OFFLINE_CHARGING_STATIONS, error);
@@ -947,6 +1109,14 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendBillingSynchronizationFailed',
+ action: ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED, error);
@@ -989,6 +1159,14 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendBillingInvoicesSynchronizationFailed',
+ action: ServerAction.BILLING_INVOICE_SYNCHRONIZATION_FAILED,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED, error);
@@ -1031,6 +1209,14 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendBillingPeriodicOperationFailed',
+ action: ServerAction.BILLING_PERFORM_OPERATIONS,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_PERFORM_OPERATIONS, error);
@@ -1060,7 +1246,7 @@ export default class NotificationHandler {
if (!hasBeenNotified) {
// Save
await NotificationHandler.saveNotification(
- tenant, notificationSource.channel, null, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED);
+ tenant, notificationSource.channel, null, ServerAction.CAR_CATALOG_SYNCHRONIZATION_FAILED);
// Send
for (const adminUser of adminUsers) {
// Enabled?
@@ -1069,6 +1255,14 @@ export default class NotificationHandler {
sourceData, adminUser, Constants.DEFAULT_TENANT_OBJECT, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendCarsSynchronizationFailed',
+ action: ServerAction.CAR_CATALOG_SYNCHRONIZATION_FAILED,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(Constants.DEFAULT_TENANT, ServerAction.CAR_CATALOG_SYNCHRONIZATION_FAILED, error);
@@ -1111,6 +1305,18 @@ export default class NotificationHandler {
sourceData, adminUser, tenant, NotificationSeverity.ERROR);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendComputeAndApplyChargingProfilesFailed',
+ action: ServerAction.COMPUTE_AND_APPLY_CHARGING_PROFILES_FAILED,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.COMPUTE_AND_APPLY_CHARGING_PROFILES_FAILED, error);
@@ -1190,9 +1396,21 @@ export default class NotificationHandler {
// Send
await notificationSource.notificationTask.sendSessionNotStarted(sourceData, sourceData.user, tenant, NotificationSeverity.INFO);
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ chargingStationID: chargingStation.id,
+ companyID: chargingStation.companyID,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ source: chargingStation.id,
+ module: MODULE_NAME, method: 'sendSessionNotStarted',
+ action: ServerAction.SESSION_NOT_STARTED_AFTER_AUTHORIZE,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
- await Logging.logActionExceptionMessage(tenant.id, ServerAction.OPTIMAL_CHARGE_REACHED, error);
+ await Logging.logActionExceptionMessage(tenant.id, ServerAction.SESSION_NOT_STARTED_AFTER_AUTHORIZE, error);
}
}
}
@@ -1227,6 +1445,15 @@ export default class NotificationHandler {
sourceData, user, tenant, NotificationSeverity.INFO);
}
}
+ } else {
+ await Logging.logDebug({
+ tenantID: tenant.id,
+ source: Constants.CENTRAL_SERVER,
+ module: MODULE_NAME, method: 'sendBillingNewInvoiceNotification',
+ action: ServerAction.BILLING_NEW_INVOICE,
+ user: user.id,
+ message: `Notification via '${notificationSource.channel}' has already been sent`
+ });
}
} catch (error) {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_NEW_INVOICE, error);

MODIFY
src/server/ocpp/services/OCPPService.ts
src/server/ocpp/services/OCPPService.ts
@@ -8,7 +8,6 @@ import Transaction, { InactivityStatus, TransactionAction } from '../../../types
import { Action } from '../../../types/Authorization';
import Authorizations from '../../../authorization/Authorizations';
import BackendError from '../../../exception/BackendError';
-import { CarConnectorConnectionType } from '../../../types/Setting';
import CarConnectorFactory from '../../../integration/car-connector/CarConnectorFactory';
import CarStorage from '../../../storage/mongodb/CarStorage';
import ChargingStationClientFactory from '../../../client/ocpp/ChargingStationClientFactory';
@@ -490,7 +489,7 @@ export default class OCPPService {
// Save
await ChargingStationStorage.saveChargingStation(tenant, chargingStation);
// Notify
- this.notifyStartTransaction(tenant, newTransaction, chargingStation, user);
+ await this.notifyStartTransaction(tenant, newTransaction, chargingStation, user);
// Log
await Logging.logInfo({
tenantID: tenant.id,
@@ -619,7 +618,7 @@ export default class OCPPService {
// Save the transaction
await TransactionStorage.saveTransaction(tenant, transaction);
// Notify User
- this.notifyStopTransaction(tenant, chargingStation, transaction, user, alternateUser);
+ await this.notifyStopTransaction(tenant, chargingStation, transaction, user, alternateUser);
// Recompute the Smart Charging Plan
await this.triggerSmartChargingStopTransaction(tenant, chargingStation, transaction);
await Logging.logInfo({
@@ -776,7 +775,9 @@ export default class OCPPService {
private async processConnectorStatusNotification(tenant: Tenant, chargingStation: ChargingStation, statusNotification: OCPPStatusNotificationRequestExtended) {
// Get Connector
- const connector = await this.checkAndGetConnectorFromStatusNotification(tenant, chargingStation, statusNotification);
+ const { connector, ignoreStatusNotification } =
+ await this.checkAndGetConnectorFromStatusNotification(tenant, chargingStation, statusNotification);
+ if (!ignoreStatusNotification) {
// Check last Transaction
await this.checkAndUpdateLastCompletedTransaction(tenant, chargingStation, statusNotification, connector);
// Update Connector
@@ -817,6 +818,7 @@ export default class OCPPService {
// Notify Users
await this.notifyStatusNotification(tenant, chargingStation, connector, statusNotification);
}
+ }
private async processSmartChargingStatusNotification(tenant: Tenant, chargingStation: ChargingStation, connector: Connector): Promise<void> {
// Trigger Smart Charging
@@ -857,7 +859,8 @@ export default class OCPPService {
}
private async checkAndGetConnectorFromStatusNotification(tenant: Tenant, chargingStation: ChargingStation,
- statusNotification: OCPPStatusNotificationRequestExtended): Promise<Connector> {
+ statusNotification: OCPPStatusNotificationRequestExtended): Promise<{connector: Connector; ignoreStatusNotification: boolean;}> {
+ let ignoreStatusNotification = false;
let foundConnector = Utils.getConnectorFromID(chargingStation, statusNotification.connectorId);
if (!foundConnector) {
// Check backup first
@@ -888,8 +891,23 @@ export default class OCPPService {
await OCPPUtils.enrichChargingStationConnectorWithTemplate(
tenant, chargingStation, statusNotification.connectorId, chargingStationTemplate);
}
+ // Same Status Notification?
+ } else if (Utils.objectAllPropertiesAreEqual(statusNotification, foundConnector, ['status', 'info', 'errorCode', 'vendorErrorCode'])) {
+ ignoreStatusNotification = true;
+ await Logging.logWarning({
+ tenantID: tenant.id,
+ siteID: chargingStation.siteID,
+ siteAreaID: chargingStation.siteAreaID,
+ companyID: chargingStation.companyID,
+ chargingStationID: chargingStation.id,
+ source: chargingStation.id,
+ action: ServerAction.OCPP_STATUS_NOTIFICATION,
+ module: MODULE_NAME, method: 'handleStatusNotification',
+ message: `${this.buildStatusNotification(statusNotification)} has not changed and will be ignored`,
+ detailedMessages: { foundConnector, statusNotification }
+ });
}
- return foundConnector;
+ return { connector: foundConnector, ignoreStatusNotification };
}
private async checkAndUpdateLastCompletedTransaction(tenant: Tenant, chargingStation: ChargingStation,
@@ -1095,7 +1113,7 @@ export default class OCPPService {
message: `${Utils.buildConnectorInfo(connector.connectorId)} Error occurred: ${this.buildStatusNotification(statusNotification)}`
});
// Send Notification (Async)
- NotificationHandler.sendChargingStationStatusError(
+ await NotificationHandler.sendChargingStationStatusError(
tenant,
Utils.generateUUID(),
chargingStation,
@@ -1310,12 +1328,12 @@ export default class OCPPService {
}
}
- private notifyEndOfCharge(tenant: Tenant, chargingStation: ChargingStation, transaction: Transaction) {
+ private async notifyEndOfCharge(tenant: Tenant, chargingStation: ChargingStation, transaction: Transaction) {
if (this.chargingStationConfig.notifEndOfChargeEnabled && transaction.user) {
// Get the i18n lib
const i18nManager = I18nManager.getInstanceForLocale(transaction.user.locale);
// Notify (Async)
- NotificationHandler.sendEndOfCharge(
+ await NotificationHandler.sendEndOfCharge(
tenant,
transaction.id.toString() + '-EOC',
transaction.user,
@@ -1338,12 +1356,12 @@ export default class OCPPService {
}
}
- private notifyOptimalChargeReached(tenant: Tenant, chargingStation: ChargingStation, transaction: Transaction) {
+ private async notifyOptimalChargeReached(tenant: Tenant, chargingStation: ChargingStation, transaction: Transaction) {
if (this.chargingStationConfig.notifBeforeEndOfChargeEnabled && transaction.user) {
// Get the i18n lib
const i18nManager = I18nManager.getInstanceForLocale(transaction.user.locale);
// Notification Before End Of Charge (Async)
- NotificationHandler.sendOptimalChargeReached(
+ await NotificationHandler.sendOptimalChargeReached(
tenant,
transaction.id.toString() + '-OCR',
transaction.user,
@@ -1373,11 +1391,11 @@ export default class OCPPService {
// Check if battery is full (100%)
if (transaction.currentStateOfCharge === 100) {
// Send Notification
- this.notifyEndOfCharge(tenant, chargingStation, transaction);
+ await this.notifyEndOfCharge(tenant, chargingStation, transaction);
// Check if optimal charge has been reached (85%)
} else if (transaction.currentStateOfCharge >= this.chargingStationConfig.notifBeforeEndOfChargePercent) {
// Send Notification
- this.notifyOptimalChargeReached(tenant, chargingStation, transaction);
+ await this.notifyOptimalChargeReached(tenant, chargingStation, transaction);
}
// No battery information: check last consumptions
} else {
@@ -1396,7 +1414,7 @@ export default class OCPPService {
consumption.limitAmps >= StaticLimitAmps.MIN_LIMIT_PER_PHASE * Utils.getNumberOfConnectedPhases(chargingStation, null, transaction.connectorId)));
// Send Notification
if (noConsumption) {
- this.notifyEndOfCharge(tenant, chargingStation, transaction);
+ await this.notifyEndOfCharge(tenant, chargingStation, transaction);
}
}
}
@@ -1618,9 +1636,9 @@ export default class OCPPService {
} while (activeTransaction);
}
- private notifyStartTransaction(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation, user: User) {
+ private async notifyStartTransaction(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation, user: User) {
if (user) {
- NotificationHandler.sendSessionStarted(
+ await NotificationHandler.sendSessionStarted(
tenant,
transaction.id.toString(),
user,
@@ -1661,13 +1679,13 @@ export default class OCPPService {
return transaction.tagID;
}
- private notifyStopTransaction(tenant: Tenant, chargingStation: ChargingStation, transaction: Transaction, user: User, alternateUser: User) {
+ private async notifyStopTransaction(tenant: Tenant, chargingStation: ChargingStation, transaction: Transaction, user: User, alternateUser: User) {
// User provided?
if (user) {
// Get the i18n lib
const i18nManager = I18nManager.getInstanceForLocale(user.locale);
// Send Notification (Async)
- NotificationHandler.sendEndOfSession(
+ await NotificationHandler.sendEndOfSession(
tenant,
transaction.id.toString() + '-EOS',
user,
@@ -1692,7 +1710,7 @@ export default class OCPPService {
// Notify Signed Data
if (transaction.stop.signedData !== '') {
// Send Notification (Async)
- NotificationHandler.sendEndOfSignedSession(
+ await NotificationHandler.sendEndOfSignedSession(
tenant,
transaction.id.toString() + '-EOSS',
user,

MODIFY
src/server/rest/v1/service/AuthService.ts
src/server/rest/v1/service/AuthService.ts
@@ -258,7 +258,7 @@ export default class AuthService {
const evseDashboardVerifyEmailURL = Utils.buildEvseURL(filteredRequest.tenant) +
'/verify-email?VerificationToken=' + verificationToken + '&Email=' + newUser.email;
// Notify (Async)
- NotificationHandler.sendNewRegisteredUser(
+ await NotificationHandler.sendNewRegisteredUser(
tenant,
Utils.generateUUID(),
newUser,
@@ -326,7 +326,7 @@ export default class AuthService {
const evseDashboardResetPassURL = Utils.buildEvseURL(filteredRequest.tenant) +
'/define-password?hash=' + resetHash;
// Send Request Password (Async)
- NotificationHandler.sendRequestPassword(
+ await NotificationHandler.sendRequestPassword(
tenant,
Utils.generateUUID(),
user,
@@ -552,7 +552,7 @@ export default class AuthService {
'User account has been successfully verified but needs an admin to activate it',
detailedMessages: { params: req.query }
});
- NotificationHandler.sendAccountVerification(
+ await NotificationHandler.sendAccountVerification(
tenant,
Utils.generateUUID(),
user,
@@ -659,7 +659,7 @@ export default class AuthService {
'/verify-email?VerificationToken=' + verificationToken + '&Email=' +
user.email;
// Send Verification Email (Async)
- NotificationHandler.sendVerificationEmail(
+ await NotificationHandler.sendVerificationEmail(
tenant,
Utils.generateUUID(),
user,

MODIFY
src/server/rest/v1/service/TenantService.ts
src/server/rest/v1/service/TenantService.ts
@@ -293,7 +293,7 @@ export default class TenantService {
'/verify-email?VerificationToken=' + verificationToken + '&Email=' +
tenantUser.email + '&ResetToken=' + resetHash;
// Send Register User (Async)
- NotificationHandler.sendNewRegisteredUser(
+ await NotificationHandler.sendNewRegisteredUser(
tenant,
Utils.generateUUID(),
tenantUser,

MODIFY
src/server/rest/v1/service/UserService.ts
src/server/rest/v1/service/UserService.ts
@@ -231,7 +231,7 @@ export default class UserService {
// Notify
if (statusHasChanged && req.tenant.id !== Constants.DEFAULT_TENANT) {
// Send notification (Async)
- NotificationHandler.sendUserAccountStatusChanged(
+ await NotificationHandler.sendUserAccountStatusChanged(
req.tenant,
Utils.generateUUID(),
user,

MODIFY
src/utils/Utils.ts
src/utils/Utils.ts
@@ -294,6 +294,15 @@ export default class Utils {
return obj == null;
}
+ public static objectAllPropertiesAreEqual(doc1: Record<string, any>, doc2: Record<string, any>, properties: string[]): boolean {
+ for (const property of properties) {
+ if ((doc1[property] !== doc2[property] && (!Utils.isNullOrUndefined(doc1[property]) || !Utils.isNullOrUndefined(doc2[property])))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public static getConnectorStatusesFromChargingStations(chargingStations: ChargingStation[]): ConnectorStats {
const connectorStats: ConnectorStats = {
totalChargers: 0,

Send notification once every 5 mins for admin
Ignore the Status Notif with same values
Log when a notification is not sent
Made Notification sync