@open-charging-cloud/chargy-core 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -10
- package/dist/Alfen.d.ts.map +1 -1
- package/dist/OCMF.d.ts +15 -2
- package/dist/OCMF.d.ts.map +1 -1
- package/dist/OCMF_BET_TariffTextExtension.d.ts +33 -0
- package/dist/OCMF_BET_TariffTextExtension.d.ts.map +1 -0
- package/dist/PTBContainer.d.ts +49 -0
- package/dist/PTBContainer.d.ts.map +1 -0
- package/dist/browser/index.js +410 -22
- package/dist/browser/index.js.map +1 -1
- package/dist/chargy.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/interfaces/chargyInterfaces.d.ts +17 -14
- package/dist/interfaces/chargyInterfaces.d.ts.map +1 -1
- package/dist/node/index.js +410 -22
- package/dist/node/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/verificationResults.d.ts +0 -2
- package/dist/verificationResults.d.ts.map +0 -1
package/dist/node/index.js
CHANGED
|
@@ -9294,8 +9294,7 @@ var Alfen = class {
|
|
|
9294
9294
|
"connectors": [{
|
|
9295
9295
|
"type": asString(connector?.["type"]) ?? "",
|
|
9296
9296
|
"cable": {
|
|
9297
|
-
"length": asNumber(connector?.["cableLength"]) ?? 0
|
|
9298
|
-
"looses": asNumber(connector?.["cableLooses"]) ?? 0
|
|
9297
|
+
"length": asNumber(connector?.["cableLength"]) ?? 0
|
|
9299
9298
|
}
|
|
9300
9299
|
}],
|
|
9301
9300
|
"energyMeters": [
|
|
@@ -12890,6 +12889,132 @@ function numberToBytesBE(value, length) {
|
|
|
12890
12889
|
}
|
|
12891
12890
|
return bytes;
|
|
12892
12891
|
}
|
|
12892
|
+
var OCMFBonnTariffParseError = class extends Error {
|
|
12893
|
+
tariffText;
|
|
12894
|
+
constructor(tariffText, message) {
|
|
12895
|
+
super(message);
|
|
12896
|
+
this.name = "OCMFBonnTariffParseError";
|
|
12897
|
+
this.tariffText = tariffText;
|
|
12898
|
+
}
|
|
12899
|
+
};
|
|
12900
|
+
function parseCents(value, tariffText, fieldName) {
|
|
12901
|
+
if (!/^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(value))
|
|
12902
|
+
throw new OCMFBonnTariffParseError(tariffText, `${fieldName} must be a non-negative decimal number`);
|
|
12903
|
+
const parsedValue = Number(value);
|
|
12904
|
+
if (!Number.isFinite(parsedValue))
|
|
12905
|
+
throw new OCMFBonnTariffParseError(tariffText, `${fieldName} is outside the supported numeric range`);
|
|
12906
|
+
return parsedValue;
|
|
12907
|
+
}
|
|
12908
|
+
function parseOCMFBonnTariffText(tariffText) {
|
|
12909
|
+
const fields = tariffText.split(";");
|
|
12910
|
+
const code = fields[0];
|
|
12911
|
+
if (fields[1] !== "EUR")
|
|
12912
|
+
throw new OCMFBonnTariffParseError(tariffText, "currency must be EUR");
|
|
12913
|
+
switch (code) {
|
|
12914
|
+
case "001":
|
|
12915
|
+
if (fields.length !== 6)
|
|
12916
|
+
throw new OCMFBonnTariffParseError(tariffText, "profile 001 must contain six fields");
|
|
12917
|
+
return {
|
|
12918
|
+
raw: tariffText,
|
|
12919
|
+
code,
|
|
12920
|
+
currency: "EUR",
|
|
12921
|
+
startFeeCents: parseCents(fields[2] ?? "", tariffText, "W"),
|
|
12922
|
+
energyFeeCentsPerKWh: parseCents(fields[3] ?? "", tariffText, "X"),
|
|
12923
|
+
blockingFeeCentsPerMinute: parseCents(fields[4] ?? "", tariffText, "Y"),
|
|
12924
|
+
blockingFeeStartMinute: parseCents(fields[5] ?? "", tariffText, "Z")
|
|
12925
|
+
};
|
|
12926
|
+
case "002":
|
|
12927
|
+
if (fields.length !== 5)
|
|
12928
|
+
throw new OCMFBonnTariffParseError(tariffText, "profile 002 must contain five fields");
|
|
12929
|
+
return {
|
|
12930
|
+
raw: tariffText,
|
|
12931
|
+
code,
|
|
12932
|
+
currency: "EUR",
|
|
12933
|
+
startFeeCents: parseCents(fields[2] ?? "", tariffText, "W"),
|
|
12934
|
+
energyFeeCentsPerKWh: parseCents(fields[3] ?? "", tariffText, "X"),
|
|
12935
|
+
blockingFeeCentsPerMinute: parseCents(fields[4] ?? "", tariffText, "Y"),
|
|
12936
|
+
blockingFeeStartsAfterCharging: true
|
|
12937
|
+
};
|
|
12938
|
+
case "003":
|
|
12939
|
+
if (fields.length !== 4)
|
|
12940
|
+
throw new OCMFBonnTariffParseError(tariffText, "profile 003 must contain four fields");
|
|
12941
|
+
return {
|
|
12942
|
+
raw: tariffText,
|
|
12943
|
+
code,
|
|
12944
|
+
currency: "EUR",
|
|
12945
|
+
startFeeCents: parseCents(fields[2] ?? "", tariffText, "W"),
|
|
12946
|
+
timeFeeCentsPerMinute: parseCents(fields[3] ?? "", tariffText, "X")
|
|
12947
|
+
};
|
|
12948
|
+
default:
|
|
12949
|
+
throw new OCMFBonnTariffParseError(tariffText, "unknown Bonn tariff profile");
|
|
12950
|
+
}
|
|
12951
|
+
}
|
|
12952
|
+
function tryParseOCMFBonnTariffText(tariffText) {
|
|
12953
|
+
try {
|
|
12954
|
+
return parseOCMFBonnTariffText(tariffText);
|
|
12955
|
+
} catch (error) {
|
|
12956
|
+
if (error instanceof OCMFBonnTariffParseError)
|
|
12957
|
+
return void 0;
|
|
12958
|
+
throw error;
|
|
12959
|
+
}
|
|
12960
|
+
}
|
|
12961
|
+
function priceComponent(type, price, stepSize) {
|
|
12962
|
+
return {
|
|
12963
|
+
type,
|
|
12964
|
+
price,
|
|
12965
|
+
step_size: stepSize
|
|
12966
|
+
};
|
|
12967
|
+
}
|
|
12968
|
+
function eurosFromCents(cents) {
|
|
12969
|
+
return new Decimal(cents).dividedBy(100);
|
|
12970
|
+
}
|
|
12971
|
+
function eurosPerHourFromCentsPerMinute(cents) {
|
|
12972
|
+
return eurosFromCents(cents).times(60);
|
|
12973
|
+
}
|
|
12974
|
+
function ocmfBonnTariffToChargingTariff(tariff) {
|
|
12975
|
+
const baseComponents = new Array(
|
|
12976
|
+
priceComponent("FLAT", eurosFromCents(tariff.startFeeCents), 1)
|
|
12977
|
+
);
|
|
12978
|
+
const elements = new Array();
|
|
12979
|
+
switch (tariff.code) {
|
|
12980
|
+
case "001":
|
|
12981
|
+
baseComponents.push(priceComponent("ENERGY", eurosFromCents(tariff.energyFeeCentsPerKWh), 1));
|
|
12982
|
+
elements.push(
|
|
12983
|
+
{ price_components: baseComponents },
|
|
12984
|
+
{
|
|
12985
|
+
price_components: [
|
|
12986
|
+
priceComponent("PARKING_TIME", eurosPerHourFromCentsPerMinute(tariff.blockingFeeCentsPerMinute), 60)
|
|
12987
|
+
],
|
|
12988
|
+
restrictions: {
|
|
12989
|
+
min_duration: tariff.blockingFeeStartMinute * 60
|
|
12990
|
+
}
|
|
12991
|
+
}
|
|
12992
|
+
);
|
|
12993
|
+
break;
|
|
12994
|
+
case "002":
|
|
12995
|
+
baseComponents.push(priceComponent("ENERGY", eurosFromCents(tariff.energyFeeCentsPerKWh), 1));
|
|
12996
|
+
elements.push(
|
|
12997
|
+
{ price_components: baseComponents },
|
|
12998
|
+
{
|
|
12999
|
+
price_components: [
|
|
13000
|
+
priceComponent("PARKING_TIME", eurosPerHourFromCentsPerMinute(tariff.blockingFeeCentsPerMinute), 60)
|
|
13001
|
+
]
|
|
13002
|
+
}
|
|
13003
|
+
);
|
|
13004
|
+
break;
|
|
13005
|
+
case "003":
|
|
13006
|
+
baseComponents.push(priceComponent("TIME", eurosPerHourFromCentsPerMinute(tariff.timeFeeCentsPerMinute), 60));
|
|
13007
|
+
elements.push({ price_components: baseComponents });
|
|
13008
|
+
break;
|
|
13009
|
+
}
|
|
13010
|
+
return {
|
|
13011
|
+
"@id": tariff.raw,
|
|
13012
|
+
currency: tariff.currency,
|
|
13013
|
+
elements
|
|
13014
|
+
};
|
|
13015
|
+
}
|
|
13016
|
+
|
|
13017
|
+
// src/OCMF.ts
|
|
12893
13018
|
var OCMFv1_x = class extends ACrypt {
|
|
12894
13019
|
curve = new this.chargy.elliptic.ec("p256");
|
|
12895
13020
|
constructor(chargy) {
|
|
@@ -12979,6 +13104,8 @@ var OCMFv1_x = class extends ACrypt {
|
|
|
12979
13104
|
return "Reading Current Type";
|
|
12980
13105
|
case "CL":
|
|
12981
13106
|
return "Cumulated Loss";
|
|
13107
|
+
case "EI":
|
|
13108
|
+
return "Error Index";
|
|
12982
13109
|
case "EF":
|
|
12983
13110
|
return "Error Flags";
|
|
12984
13111
|
case "ST":
|
|
@@ -13186,10 +13313,39 @@ var OCMF = class {
|
|
|
13186
13313
|
const identificationType = firstOCMDJSONDocument.payload.IT;
|
|
13187
13314
|
const identificationData = firstOCMDJSONDocument.payload.ID;
|
|
13188
13315
|
const tariffText = firstOCMDJSONDocument.payload.TT;
|
|
13189
|
-
const
|
|
13316
|
+
const tariffTextInterpretation = typeof tariffText === "string" ? tryParseOCMFBonnTariffText(tariffText) : void 0;
|
|
13317
|
+
const chargingTariff = typeof tariffText === "string" && tariffText.length > 0 ? tariffTextInterpretation !== void 0 ? ocmfBonnTariffToChargingTariff(tariffTextInterpretation) : { "@id": tariffText } : void 0;
|
|
13318
|
+
const controllerFirmwareVersion = firstOCMDJSONDocument.payload.CF;
|
|
13190
13319
|
const lossCompensation = firstOCMDJSONDocument.payload.LC;
|
|
13320
|
+
const signedCable = lossCompensation !== void 0 && typeof lossCompensation.LR === "number" && Number.isFinite(lossCompensation.LR) && typeof lossCompensation.LU === "string" && lossCompensation.LU.length > 0 ? {
|
|
13321
|
+
...typeof lossCompensation.LN === "string" ? { lossCompensation: lossCompensation.LN } : {},
|
|
13322
|
+
...typeof lossCompensation.LI === "number" ? { lossCompensationId: lossCompensation.LI.toString() } : {},
|
|
13323
|
+
resistance: lossCompensation.LR,
|
|
13324
|
+
resistanceUnit: lossCompensation.LU
|
|
13325
|
+
} : void 0;
|
|
13191
13326
|
const chargePointIdType = firstOCMDJSONDocument.payload.CT;
|
|
13192
13327
|
const chargePointId = firstOCMDJSONDocument.payload.CI;
|
|
13328
|
+
let signedChargingStationId;
|
|
13329
|
+
let signedEVSEId;
|
|
13330
|
+
let signedConnectorId;
|
|
13331
|
+
if (typeof chargePointId === "string" && chargePointId.trim().length > 0) {
|
|
13332
|
+
const normalizedChargePointId = chargePointId.trim();
|
|
13333
|
+
switch (chargePointIdType?.toUpperCase()) {
|
|
13334
|
+
case void 0:
|
|
13335
|
+
break;
|
|
13336
|
+
case "EVSEID":
|
|
13337
|
+
signedEVSEId = normalizedChargePointId;
|
|
13338
|
+
break;
|
|
13339
|
+
case "CBIDC": {
|
|
13340
|
+
const cbidcMatch = /^(\S+)\s+(\S+)$/.exec(normalizedChargePointId);
|
|
13341
|
+
if (cbidcMatch?.[1] !== void 0 && cbidcMatch[2] !== void 0) {
|
|
13342
|
+
signedChargingStationId = cbidcMatch[1];
|
|
13343
|
+
signedConnectorId = cbidcMatch[2];
|
|
13344
|
+
}
|
|
13345
|
+
break;
|
|
13346
|
+
}
|
|
13347
|
+
}
|
|
13348
|
+
}
|
|
13193
13349
|
if (isOptionalString(formatVersion) && isOptionalString(gatewayInformation) && isOptionalString(gatewaySerial) && isOptionalString(gatewayVersion) && isMandatoryString(paging) && isOptionalString(meterVendor) && isOptionalString(meterModel) && // OCMF 1.0 table 3 lists MS as 1..1, but the later "Relation of Serial Numbers,
|
|
13194
13350
|
// Charge Point and Public Key" section makes the serial-number fields conditionally
|
|
13195
13351
|
// mandatory. KEBA KCP30 records identify the signing gateway via GS and omit MS.
|
|
@@ -13202,7 +13358,7 @@ var OCMF = class {
|
|
|
13202
13358
|
// IT is 1..1 in the user-assignment table for transaction records. Some OCMF 1.0
|
|
13203
13359
|
// implementations omit it when no user is assigned (IS=false, ID empty). We tolerate
|
|
13204
13360
|
// this vendor compatibility case instead of rejecting otherwise valid signatures.
|
|
13205
|
-
isOptionalString(identificationType) && isOptionalString(identificationData) && isOptionalString(tariffText) && isOptionalString(
|
|
13361
|
+
isOptionalString(identificationType) && isOptionalString(identificationData) && isOptionalString(tariffText) && isOptionalString(controllerFirmwareVersion) && isOptionalJSONObject(lossCompensation) && isOptionalString(chargePointIdType) && isOptionalString(chargePointId)) {
|
|
13206
13362
|
const paginationPrefix = paging.length > 0 ? paging.charAt(0).toLowerCase() : null;
|
|
13207
13363
|
const transactionType = paginationPrefix === "t" ? "transaction" /* transaction */ : paginationPrefix === "f" ? "fiscal" /* fiscal */ : "undefined" /* undefined */;
|
|
13208
13364
|
const pagination = paging.length > 1 ? parseNumber(paging.substring(1)) : null;
|
|
@@ -13234,11 +13390,22 @@ var OCMF = class {
|
|
|
13234
13390
|
"@id": identificationData ?? "?",
|
|
13235
13391
|
"type": identificationType ?? "?"
|
|
13236
13392
|
},
|
|
13393
|
+
"chargingTariffs": chargingTariff !== void 0 ? [chargingTariff] : void 0,
|
|
13237
13394
|
"ocmf": {
|
|
13238
13395
|
"formatVersion": formatVersion,
|
|
13239
13396
|
"gatewayInformation": gatewayInformation,
|
|
13240
13397
|
"gatewaySerial": gatewaySerial,
|
|
13241
|
-
"gatewayVersion": gatewayVersion
|
|
13398
|
+
"gatewayVersion": gatewayVersion,
|
|
13399
|
+
"meterVendor": meterVendor,
|
|
13400
|
+
"meterModel": meterModel,
|
|
13401
|
+
"meterSerial": meterSerial,
|
|
13402
|
+
"meterFirmware": meterFirmware,
|
|
13403
|
+
"tariffText": tariffText,
|
|
13404
|
+
"tariffTextInterpretation": tariffTextInterpretation,
|
|
13405
|
+
"controllerFirmwareVersion": controllerFirmwareVersion,
|
|
13406
|
+
"lossCompensation": lossCompensation,
|
|
13407
|
+
"chargePointIdentificationType": chargePointIdType,
|
|
13408
|
+
"chargePointIdentification": chargePointId
|
|
13242
13409
|
},
|
|
13243
13410
|
// "chargingStationOperators": [{
|
|
13244
13411
|
// "chargingPools": [{
|
|
@@ -13305,6 +13472,11 @@ var OCMF = class {
|
|
|
13305
13472
|
"@context": "https://open.charging.cloud/contexts/SessionSignatureFormats/OCMFv1.0+json",
|
|
13306
13473
|
"begin": "?",
|
|
13307
13474
|
"end": "?",
|
|
13475
|
+
"chargingStationId": signedChargingStationId,
|
|
13476
|
+
"EVSEId": signedEVSEId,
|
|
13477
|
+
"ConnectorId": signedConnectorId,
|
|
13478
|
+
"tariffId": chargingTariff?.["@id"],
|
|
13479
|
+
"chargingTariffs": chargingTariff !== void 0 ? [chargingTariff] : void 0,
|
|
13308
13480
|
"authorizationStart": {
|
|
13309
13481
|
"@id": identificationData ?? "?",
|
|
13310
13482
|
"type": identificationType ?? "?",
|
|
@@ -13317,19 +13489,50 @@ var OCMF = class {
|
|
|
13317
13489
|
}],
|
|
13318
13490
|
"certainty": 1
|
|
13319
13491
|
};
|
|
13320
|
-
|
|
13492
|
+
const resolvedChargingStationId = signedChargingStationId ?? containerChargingStation?.["@id"];
|
|
13493
|
+
if (resolvedChargingStationId !== void 0) {
|
|
13494
|
+
const matchingContainerStation = containerChargingStation?.["@id"] === resolvedChargingStationId ? containerChargingStation : void 0;
|
|
13495
|
+
const resolvedChargingStation = {
|
|
13496
|
+
...matchingContainerStation ?? { "@id": resolvedChargingStationId },
|
|
13497
|
+
...controllerFirmwareVersion !== void 0 ? {
|
|
13498
|
+
firmware: {
|
|
13499
|
+
...matchingContainerStation?.firmware,
|
|
13500
|
+
version: controllerFirmwareVersion
|
|
13501
|
+
}
|
|
13502
|
+
} : {}
|
|
13503
|
+
};
|
|
13504
|
+
CTR.chargingStations = [
|
|
13505
|
+
resolvedChargingStation,
|
|
13506
|
+
...ContainerInfos?.chargingStations?.filter((station) => station["@id"] !== resolvedChargingStationId) ?? []
|
|
13507
|
+
];
|
|
13508
|
+
if (CTR.chargingSessions?.[0] !== void 0) {
|
|
13509
|
+
CTR.chargingSessions[0].chargingStationId ??= resolvedChargingStationId;
|
|
13510
|
+
CTR.chargingSessions[0].chargingStation = resolvedChargingStation;
|
|
13511
|
+
}
|
|
13512
|
+
} else if (ContainerInfos?.chargingStations !== void 0)
|
|
13321
13513
|
CTR.chargingStations = ContainerInfos.chargingStations;
|
|
13322
|
-
if (containerChargingStation !== void 0 && CTR.chargingSessions?.[0] !== void 0) {
|
|
13323
|
-
CTR.chargingSessions[0].chargingStationId = containerChargingStation["@id"];
|
|
13324
|
-
CTR.chargingSessions[0].chargingStation = containerChargingStation;
|
|
13325
|
-
}
|
|
13326
13514
|
if (containerEVSE !== void 0 && CTR.chargingSessions?.[0] !== void 0) {
|
|
13327
|
-
CTR.chargingSessions[0]
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13515
|
+
const chargingSession = CTR.chargingSessions[0];
|
|
13516
|
+
chargingSession.EVSEId ??= containerEVSE["@id"];
|
|
13517
|
+
if (chargingSession.EVSEId === containerEVSE["@id"])
|
|
13518
|
+
chargingSession.EVSE = containerEVSE;
|
|
13519
|
+
}
|
|
13520
|
+
const resolvedConnectorId = signedConnectorId ?? containerConnector?.["@id"];
|
|
13521
|
+
if ((resolvedConnectorId !== void 0 || signedCable !== void 0) && CTR.chargingSessions?.[0] !== void 0) {
|
|
13522
|
+
const matchingContainerConnector = containerConnector?.["@id"] === resolvedConnectorId ? containerConnector : void 0;
|
|
13523
|
+
const resolvedConnector = {
|
|
13524
|
+
...matchingContainerConnector,
|
|
13525
|
+
...resolvedConnectorId !== void 0 ? { "@id": resolvedConnectorId } : {},
|
|
13526
|
+
...signedCable !== void 0 ? {
|
|
13527
|
+
cable: {
|
|
13528
|
+
...matchingContainerConnector?.cable,
|
|
13529
|
+
...signedCable
|
|
13530
|
+
}
|
|
13531
|
+
} : {}
|
|
13532
|
+
};
|
|
13533
|
+
const chargingSession = CTR.chargingSessions[0];
|
|
13534
|
+
chargingSession.ConnectorId ??= resolvedConnectorId;
|
|
13535
|
+
chargingSession.Connector = resolvedConnector;
|
|
13333
13536
|
}
|
|
13334
13537
|
const measurementsByKey = /* @__PURE__ */ new Map();
|
|
13335
13538
|
for (const ocmfJSONDocument of OCMFJSONDocuments) {
|
|
@@ -13343,12 +13546,13 @@ var OCMF = class {
|
|
|
13343
13546
|
const readingUnit = effectiveReading.RU;
|
|
13344
13547
|
const readingCurrentType = effectiveReading.RT;
|
|
13345
13548
|
const cumulatedLoss = effectiveReading.CL;
|
|
13549
|
+
const errorIndex = effectiveReading.EI;
|
|
13346
13550
|
const errorFlags = effectiveReading.EF;
|
|
13347
13551
|
const status = effectiveReading.ST;
|
|
13348
13552
|
inheritedReading = effectiveReading;
|
|
13349
13553
|
if (isMandatoryString(time) && isOptionalString(transaction) && isMandatoryDecimal(readingValue) && // Note: Some vendors use a JSON string here!
|
|
13350
13554
|
isOptionalString(readingIdentification) && isMandatoryString(readingUnit) && isOptionalString(readingCurrentType) && // chargyLib.isOptionalDecimal (cumulatedLoss) &&
|
|
13351
|
-
isOptionalString(errorFlags) && isMandatoryString(status)) {
|
|
13555
|
+
isOptionalNumber(errorIndex) && isOptionalString(errorFlags) && isMandatoryString(status)) {
|
|
13352
13556
|
const timeSplit = time.split(" ");
|
|
13353
13557
|
if (timeSplit.length != 2) return {
|
|
13354
13558
|
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
@@ -13447,6 +13651,7 @@ var OCMF = class {
|
|
|
13447
13651
|
// "T" ToDo: Serialize this to a string!
|
|
13448
13652
|
"pagination": pagination,
|
|
13449
13653
|
// "9289"
|
|
13654
|
+
"errorIndex": errorIndex,
|
|
13450
13655
|
"errorFlags": errorFlags,
|
|
13451
13656
|
// ""
|
|
13452
13657
|
"cumulatedLoss": cumulatedLoss != null && cumulatedLoss !== 0 ? new Decimal(cumulatedLoss) : void 0,
|
|
@@ -13463,10 +13668,8 @@ var OCMF = class {
|
|
|
13463
13668
|
}
|
|
13464
13669
|
}
|
|
13465
13670
|
}
|
|
13466
|
-
if (ContainerInfos?.chargingStations !== void 0)
|
|
13467
|
-
CTR.chargingStations = ContainerInfos.chargingStations;
|
|
13468
13671
|
if (ContainerInfos?.warnings !== void 0)
|
|
13469
|
-
CTR.warnings =
|
|
13672
|
+
CTR.warnings = (CTR.warnings ?? []).concat(ContainerInfos.warnings);
|
|
13470
13673
|
CTR.status = OCMFJSONDocuments.every((ocmfJSONDocument) => ocmfJSONDocument.validationStatus === "ValidSignature" /* ValidSignature */) ? "ValidSignature" /* ValidSignature */ : "InvalidSignature" /* InvalidSignature */;
|
|
13471
13674
|
if (CTR.chargingSessions != null && CTR.chargingSessions.length > 0 && CTR.chargingSessions[0]) {
|
|
13472
13675
|
CTR.begin = CTR.chargingSessions[0].begin;
|
|
@@ -13961,6 +14164,11 @@ var OCMF = class {
|
|
|
13961
14164
|
}
|
|
13962
14165
|
if (ocmfJSONDocumentGroup[0]) {
|
|
13963
14166
|
switch (ocmfJSONDocumentGroup[0].payload.FV) {
|
|
14167
|
+
// FV has cardinality 0..1 in OCMF. All supported 1.x
|
|
14168
|
+
// versions use the same parser, so an omitted version
|
|
14169
|
+
// is parsed as generic OCMF without changing the
|
|
14170
|
+
// signed payload or inventing a concrete version.
|
|
14171
|
+
case void 0:
|
|
13964
14172
|
case "0.1":
|
|
13965
14173
|
// OCMF 0.1 SAFE reference data uses a few legacy field names/forms (VI/VV,
|
|
13966
14174
|
// string based IS values), but the compact signed document structure is close
|
|
@@ -14982,6 +15190,184 @@ function readDERInteger(bytes, getOffset, setOffset) {
|
|
|
14982
15190
|
return hex;
|
|
14983
15191
|
}
|
|
14984
15192
|
|
|
15193
|
+
// src/PTBContainer.ts
|
|
15194
|
+
var base64RegExp = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
15195
|
+
var formatVersionRegExp = /^1(?:\.[0-9]+)?$/;
|
|
15196
|
+
var PTB = class {
|
|
15197
|
+
chargy;
|
|
15198
|
+
constructor(chargy) {
|
|
15199
|
+
this.chargy = chargy;
|
|
15200
|
+
}
|
|
15201
|
+
async TryToParsePTBContainer(container) {
|
|
15202
|
+
const validation = this.validateContainer(container);
|
|
15203
|
+
if (!validation.valid)
|
|
15204
|
+
return this.validationError(validation.issues);
|
|
15205
|
+
const ptbContainer = validation.container;
|
|
15206
|
+
const containerInfos = {
|
|
15207
|
+
chargingStations: [{
|
|
15208
|
+
"@id": ptbContainer.chargeboxIdentifier,
|
|
15209
|
+
address: this.normalizeAddress(ptbContainer.address),
|
|
15210
|
+
geoLocation: {
|
|
15211
|
+
lat: ptbContainer.geoLocation.lat,
|
|
15212
|
+
lng: ptbContainer.geoLocation.lng
|
|
15213
|
+
},
|
|
15214
|
+
EVSEs: [{
|
|
15215
|
+
"@id": ptbContainer.chargeboxIdentifier
|
|
15216
|
+
}]
|
|
15217
|
+
}]
|
|
15218
|
+
};
|
|
15219
|
+
return new OCMF(this.chargy).TryToParseOCMFDocuments(
|
|
15220
|
+
[ptbContainer.ocmfBegin, ptbContainer.ocmfEnd],
|
|
15221
|
+
ptbContainer.publicKey,
|
|
15222
|
+
"base64",
|
|
15223
|
+
containerInfos
|
|
15224
|
+
);
|
|
15225
|
+
}
|
|
15226
|
+
validateContainer(container) {
|
|
15227
|
+
const issues = [];
|
|
15228
|
+
if (!isMandatoryJSONObject(container))
|
|
15229
|
+
return {
|
|
15230
|
+
valid: false,
|
|
15231
|
+
issues: [{
|
|
15232
|
+
path: "$",
|
|
15233
|
+
message: "must be an object"
|
|
15234
|
+
}]
|
|
15235
|
+
};
|
|
15236
|
+
this.requireConstantString(container, "format", "ptb", issues);
|
|
15237
|
+
this.requireString(container, "publicKey", issues);
|
|
15238
|
+
this.requireString(container, "chargeboxIdentifier", issues);
|
|
15239
|
+
this.requireString(container, "ocmfBegin", issues);
|
|
15240
|
+
this.requireString(container, "ocmfEnd", issues);
|
|
15241
|
+
const formatVersion = container["formatVersion"];
|
|
15242
|
+
if (formatVersion !== void 0 && (typeof formatVersion !== "string" || !formatVersionRegExp.test(formatVersion))) {
|
|
15243
|
+
issues.push({
|
|
15244
|
+
path: "$.formatVersion",
|
|
15245
|
+
message: "must match ^1(?:\\.[0-9]+)?$"
|
|
15246
|
+
});
|
|
15247
|
+
}
|
|
15248
|
+
const publicKey = container["publicKey"];
|
|
15249
|
+
if (typeof publicKey === "string" && publicKey.length > 0 && !base64RegExp.test(publicKey))
|
|
15250
|
+
issues.push({
|
|
15251
|
+
path: "$.publicKey",
|
|
15252
|
+
message: "must be a base64 encoded string"
|
|
15253
|
+
});
|
|
15254
|
+
for (const propertyName of ["ocmfBegin", "ocmfEnd"]) {
|
|
15255
|
+
const ocmfDocument = container[propertyName];
|
|
15256
|
+
if (typeof ocmfDocument === "string" && (ocmfDocument.length < 10 || !ocmfDocument.startsWith("OCMF|"))) {
|
|
15257
|
+
issues.push({
|
|
15258
|
+
path: "$." + propertyName,
|
|
15259
|
+
message: "must be an unmodified OCMF record beginning with OCMF|"
|
|
15260
|
+
});
|
|
15261
|
+
}
|
|
15262
|
+
}
|
|
15263
|
+
const address = container["address"];
|
|
15264
|
+
if (!isMandatoryJSONObject(address))
|
|
15265
|
+
issues.push({
|
|
15266
|
+
path: "$.address",
|
|
15267
|
+
message: "must be an object"
|
|
15268
|
+
});
|
|
15269
|
+
else
|
|
15270
|
+
this.validateAddress(address, issues);
|
|
15271
|
+
const geoLocation = container["geoLocation"];
|
|
15272
|
+
if (!isMandatoryJSONObject(geoLocation))
|
|
15273
|
+
issues.push({
|
|
15274
|
+
path: "$.geoLocation",
|
|
15275
|
+
message: "must be an object"
|
|
15276
|
+
});
|
|
15277
|
+
else
|
|
15278
|
+
this.validateGeoLocation(geoLocation, issues);
|
|
15279
|
+
if (issues.length > 0)
|
|
15280
|
+
return {
|
|
15281
|
+
valid: false,
|
|
15282
|
+
issues
|
|
15283
|
+
};
|
|
15284
|
+
return {
|
|
15285
|
+
valid: true,
|
|
15286
|
+
container
|
|
15287
|
+
};
|
|
15288
|
+
}
|
|
15289
|
+
validateAddress(address, issues) {
|
|
15290
|
+
this.requireString(address, "street", issues, "$.address");
|
|
15291
|
+
for (const propertyName of ["houseNumber", "zipCode", "postalCode", "town", "city", "country"]) {
|
|
15292
|
+
const propertyValue = address[propertyName];
|
|
15293
|
+
if (propertyValue !== void 0 && typeof propertyValue !== "string")
|
|
15294
|
+
issues.push({
|
|
15295
|
+
path: "$.address." + propertyName,
|
|
15296
|
+
message: "must be a string"
|
|
15297
|
+
});
|
|
15298
|
+
else if ((propertyName === "town" || propertyName === "city") && propertyValue === "")
|
|
15299
|
+
issues.push({
|
|
15300
|
+
path: "$.address." + propertyName,
|
|
15301
|
+
message: "must be a non-empty string"
|
|
15302
|
+
});
|
|
15303
|
+
}
|
|
15304
|
+
const town = address["town"];
|
|
15305
|
+
const city = address["city"];
|
|
15306
|
+
if ((typeof town !== "string" || town.length === 0) && (typeof city !== "string" || city.length === 0)) {
|
|
15307
|
+
issues.push({
|
|
15308
|
+
path: "$.address",
|
|
15309
|
+
message: "must contain a non-empty town or city"
|
|
15310
|
+
});
|
|
15311
|
+
}
|
|
15312
|
+
}
|
|
15313
|
+
validateGeoLocation(geoLocation, issues) {
|
|
15314
|
+
const latitude = geoLocation["lat"];
|
|
15315
|
+
const longitude = geoLocation["lng"];
|
|
15316
|
+
if (typeof latitude !== "number" || !Number.isFinite(latitude) || latitude < -90 || latitude > 90)
|
|
15317
|
+
issues.push({
|
|
15318
|
+
path: "$.geoLocation.lat",
|
|
15319
|
+
message: "must be a number between -90 and 90"
|
|
15320
|
+
});
|
|
15321
|
+
if (typeof longitude !== "number" || !Number.isFinite(longitude) || longitude < -180 || longitude > 180)
|
|
15322
|
+
issues.push({
|
|
15323
|
+
path: "$.geoLocation.lng",
|
|
15324
|
+
message: "must be a number between -180 and 180"
|
|
15325
|
+
});
|
|
15326
|
+
for (const propertyName of Object.keys(geoLocation))
|
|
15327
|
+
if (propertyName !== "lat" && propertyName !== "lng")
|
|
15328
|
+
issues.push({
|
|
15329
|
+
path: "$.geoLocation." + propertyName,
|
|
15330
|
+
message: "is not allowed"
|
|
15331
|
+
});
|
|
15332
|
+
}
|
|
15333
|
+
requireString(json, propertyName, issues, parentPath = "$") {
|
|
15334
|
+
const value = json[propertyName];
|
|
15335
|
+
if (typeof value !== "string" || value.length === 0)
|
|
15336
|
+
issues.push({
|
|
15337
|
+
path: parentPath + "." + propertyName,
|
|
15338
|
+
message: "must be a non-empty string"
|
|
15339
|
+
});
|
|
15340
|
+
}
|
|
15341
|
+
requireConstantString(json, propertyName, expectedValue, issues) {
|
|
15342
|
+
if (json[propertyName] !== expectedValue)
|
|
15343
|
+
issues.push({
|
|
15344
|
+
path: "$." + propertyName,
|
|
15345
|
+
message: "must equal " + expectedValue
|
|
15346
|
+
});
|
|
15347
|
+
}
|
|
15348
|
+
normalizeAddress(address) {
|
|
15349
|
+
return {
|
|
15350
|
+
city: address.city ?? address.town,
|
|
15351
|
+
street: address.street,
|
|
15352
|
+
houseNumber: address.houseNumber,
|
|
15353
|
+
postalCode: address.postalCode ?? address.zipCode,
|
|
15354
|
+
country: address.country
|
|
15355
|
+
};
|
|
15356
|
+
}
|
|
15357
|
+
validationError(issues) {
|
|
15358
|
+
return {
|
|
15359
|
+
format: "ptb",
|
|
15360
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
15361
|
+
message: this.chargy.GetMultilanguageText("Invalid PTB OCMF container!"),
|
|
15362
|
+
certainty: 1,
|
|
15363
|
+
issues,
|
|
15364
|
+
errors: issues.map((issue) => CreateError(
|
|
15365
|
+
this.chargy.GetMultilanguageText(issue.path + " " + issue.message)
|
|
15366
|
+
))
|
|
15367
|
+
};
|
|
15368
|
+
}
|
|
15369
|
+
};
|
|
15370
|
+
|
|
14985
15371
|
// src/SAFE_XML.ts
|
|
14986
15372
|
var SAFEXML = class _SAFEXML {
|
|
14987
15373
|
chargy;
|
|
@@ -18208,7 +18594,9 @@ var Chargy = class {
|
|
|
18208
18594
|
if (IsAChargeTransparencyLiveLink(JSONContent)) {
|
|
18209
18595
|
JSONContent.timestamp ??= (/* @__PURE__ */ new Date()).toISOString();
|
|
18210
18596
|
processedFile.result = JSONContent;
|
|
18211
|
-
} else if (
|
|
18597
|
+
} else if (JSONContent["format"] === "ptb")
|
|
18598
|
+
processedFile.result = await new PTB(this).TryToParsePTBContainer(JSONContent);
|
|
18599
|
+
else if (isMandatoryString(JSONContext)) {
|
|
18212
18600
|
if (JSONContext.startsWith("https://open.charging.cloud/contexts/CTR+json"))
|
|
18213
18601
|
processedFile.result = JSONContent;
|
|
18214
18602
|
else if (JSONContext.startsWith("https://open.charging.cloud/contexts/publicKey+json"))
|
|
@@ -18668,6 +19056,6 @@ var Chargy = class {
|
|
|
18668
19056
|
}
|
|
18669
19057
|
};
|
|
18670
19058
|
|
|
18671
|
-
export { ACrypt, Alfen, AlfenCrypt01, BSMCrypt01, CanonicalJSONError, ChargeIT, ChargePoint, ChargePointCrypt01, IChargeTransparencyLiveLink_exports as ChargeTransparencyLiveLink, ChargeTransparencyLiveLinkContext, IChargeTransparencyRecord_exports as ChargeTransparencyRecord, Chargy, chargyInterfaces_exports as ChargyInterfaces, Clone, CloneCTR, ConcatenateBuffers, CreateDiv, CreateDiv2, CreateError, CreateWarning, CryptoAlgorithms, CryptoHashAlgorithms, DayOfWeek, DisplayPrefixes, EDL40, EDL40Crypt01, EDL40ValidationError, EDL40_OBIS, EDL40_SESSION_CONTEXT, EDL40_SIGNATURE_CONTEXT, EMHCrypt01, ErrorLevel, GDFCrypt01, IECCurves, IEncoding, InformationRelevance, InformationRelevanceToString, IsAChargeTransparencyLiveLink, IsAChargeTransparencyRecord, IsAPublicKey, IsAPublicKeyLookup, IsAPublicKeySignature, IsAPublicKeyXY, IsASessionCryptoResult, IsNullOrEmpty, JSONSignatureVerificationStatus, MENNEKES_EDL40_OBIS, MENNEKES_EDL40_XMLNS, Mennekes, MennekesCrypt01, OBIS2Hex, OBIS2MeasurementName, OBIS_RegExpr, OCMF, OCMFTransactionTypes, OCMFv1_x, OCPI, OIDInfo, PCDF, PCDFCrypt01, PCDFParseError, PCDFValidationError, PCDF_FIELD_ORDER, PCDF_PREFIX, ParseJSON_LD, PublicKeyFormats, IPublicKeyInfo_exports as PublicKeyInfo, SAFEXML, SessionVerificationResult, SetHex, SetInt8, SetText, SetText_withLength, SetTimestamp, SetTimestamp32, SetUInt32, SetUInt32_withCode, SetUInt64, SetUInt64D, SignMessage, SignatureFormats, TimeStatusTypes, UTC2human, VerificationResult, VerifyJSONMessageSignatures, WarningLevel, WhenNullOrEmpty, XMLContainer, asJSONArray, asJSONObject, asNumber, asString, base64ToBytes, buf2hex, buildEDL40Signature, buildIsaSignature, buildMennekesSignatureData, bytesToBase64, bytesToHex, canParseEDL40, canonicalJSONBytes, canonicalJSONStringify, cleanHex, closeFullscreen, createHexString, dateToMennekesLocalEpochSeconds, decodeSmlMessages, extractMennekesChargingProcesses, findEntryByObis, findGetListRes, firstKey, firstValue, getArrayElement, getArrayLikeElement, getDirectChildByLocalName, getDirectChildrenByLocalName, getElementsByLocalName, getFirstArrayElement, getInt16Bytes, getInt32Bytes, getInt64Bytes, getInt8Bytes, getLastArrayElement, getTrimmedTextContent, hashFile, hex2bin, hex32, hexToArrayBuffer, hexToBytes, intFromBytes, isEncodedValue, isGeoLocation, isI18NString, isICryptoResult, isIFileInfo, isISessionCryptoResult1, isISessionCryptoResult2, isJSONLDObject, isMandatoryArrayOfStrings, isMandatoryBoolean, isMandatoryDecimal, isMandatoryJSONArray, isMandatoryJSONObject, isMandatoryNumber, isMandatoryString, isMandatoryURL, isOIDInfo, isObject, isOptionalArrayOfStrings, isOptionalDecimal, isOptionalJSONArray, isOptionalJSONArrayError, isOptionalJSONArrayOk, isOptionalJSONObject, isOptionalNumber, isOptionalString, isOptionalStringArray, isOptionalStringOrOIDInfo, isOptionalURL, isPCDFText, isPublicKeySubject, isString, isStringArray, isStringOrOIDInfo, isStringOrStringArray, isaListNameContext, jsonPrettyPrinter, measurementName2human, normalizePCDFPublicKeyHex, normalizeXMLText, openFullscreen, pad, parseAndVerifyJSONSignatures, parseDescription, parseEDL40, parseHexString, parseMennekesXMLDocument, parseNumber, parseOBIS, parsePCDFDocument, parsePCDFPublicKey, parsePCDFSignature, parseSmlTime, parseUTC, readQRCodeTextFromImage, readQRCodeTextFromImageData, readTLV, secp224k1, setUILocale, sha256, sha256____, sha384, sha384____, sha512, sha512____, signJSONMessage, signMessage, stripPCDFControlCharacters, stripTransport, time2human, toArrayBuffer, toSessionVerificationResults, toUint8Array, transformEDL40Status, unquotePCDFText, validatePCDFFields, verifyEDL40Document, verifyJSONMessageSignatureResults, verifyJSONMessageSignatures, verifyJSONSignature, verifyJSONSignatureResult, verifyPCDFDocument };
|
|
19059
|
+
export { ACrypt, Alfen, AlfenCrypt01, BSMCrypt01, CanonicalJSONError, ChargeIT, ChargePoint, ChargePointCrypt01, IChargeTransparencyLiveLink_exports as ChargeTransparencyLiveLink, ChargeTransparencyLiveLinkContext, IChargeTransparencyRecord_exports as ChargeTransparencyRecord, Chargy, chargyInterfaces_exports as ChargyInterfaces, Clone, CloneCTR, ConcatenateBuffers, CreateDiv, CreateDiv2, CreateError, CreateWarning, CryptoAlgorithms, CryptoHashAlgorithms, DayOfWeek, DisplayPrefixes, EDL40, EDL40Crypt01, EDL40ValidationError, EDL40_OBIS, EDL40_SESSION_CONTEXT, EDL40_SIGNATURE_CONTEXT, EMHCrypt01, ErrorLevel, GDFCrypt01, IECCurves, IEncoding, InformationRelevance, InformationRelevanceToString, IsAChargeTransparencyLiveLink, IsAChargeTransparencyRecord, IsAPublicKey, IsAPublicKeyLookup, IsAPublicKeySignature, IsAPublicKeyXY, IsASessionCryptoResult, IsNullOrEmpty, JSONSignatureVerificationStatus, MENNEKES_EDL40_OBIS, MENNEKES_EDL40_XMLNS, Mennekes, MennekesCrypt01, OBIS2Hex, OBIS2MeasurementName, OBIS_RegExpr, OCMF, OCMFBonnTariffParseError, OCMFTransactionTypes, OCMFv1_x, OCPI, OIDInfo, PCDF, PCDFCrypt01, PCDFParseError, PCDFValidationError, PCDF_FIELD_ORDER, PCDF_PREFIX, PTB, ParseJSON_LD, PublicKeyFormats, IPublicKeyInfo_exports as PublicKeyInfo, SAFEXML, SessionVerificationResult, SetHex, SetInt8, SetText, SetText_withLength, SetTimestamp, SetTimestamp32, SetUInt32, SetUInt32_withCode, SetUInt64, SetUInt64D, SignMessage, SignatureFormats, TimeStatusTypes, UTC2human, VerificationResult, VerifyJSONMessageSignatures, WarningLevel, WhenNullOrEmpty, XMLContainer, asJSONArray, asJSONObject, asNumber, asString, base64ToBytes, buf2hex, buildEDL40Signature, buildIsaSignature, buildMennekesSignatureData, bytesToBase64, bytesToHex, canParseEDL40, canonicalJSONBytes, canonicalJSONStringify, cleanHex, closeFullscreen, createHexString, dateToMennekesLocalEpochSeconds, decodeSmlMessages, extractMennekesChargingProcesses, findEntryByObis, findGetListRes, firstKey, firstValue, getArrayElement, getArrayLikeElement, getDirectChildByLocalName, getDirectChildrenByLocalName, getElementsByLocalName, getFirstArrayElement, getInt16Bytes, getInt32Bytes, getInt64Bytes, getInt8Bytes, getLastArrayElement, getTrimmedTextContent, hashFile, hex2bin, hex32, hexToArrayBuffer, hexToBytes, intFromBytes, isEncodedValue, isGeoLocation, isI18NString, isICryptoResult, isIFileInfo, isISessionCryptoResult1, isISessionCryptoResult2, isJSONLDObject, isMandatoryArrayOfStrings, isMandatoryBoolean, isMandatoryDecimal, isMandatoryJSONArray, isMandatoryJSONObject, isMandatoryNumber, isMandatoryString, isMandatoryURL, isOIDInfo, isObject, isOptionalArrayOfStrings, isOptionalDecimal, isOptionalJSONArray, isOptionalJSONArrayError, isOptionalJSONArrayOk, isOptionalJSONObject, isOptionalNumber, isOptionalString, isOptionalStringArray, isOptionalStringOrOIDInfo, isOptionalURL, isPCDFText, isPublicKeySubject, isString, isStringArray, isStringOrOIDInfo, isStringOrStringArray, isaListNameContext, jsonPrettyPrinter, measurementName2human, normalizePCDFPublicKeyHex, normalizeXMLText, ocmfBonnTariffToChargingTariff, openFullscreen, pad, parseAndVerifyJSONSignatures, parseDescription, parseEDL40, parseHexString, parseMennekesXMLDocument, parseNumber, parseOBIS, parseOCMFBonnTariffText, parsePCDFDocument, parsePCDFPublicKey, parsePCDFSignature, parseSmlTime, parseUTC, readQRCodeTextFromImage, readQRCodeTextFromImageData, readTLV, secp224k1, setUILocale, sha256, sha256____, sha384, sha384____, sha512, sha512____, signJSONMessage, signMessage, stripPCDFControlCharacters, stripTransport, time2human, toArrayBuffer, toSessionVerificationResults, toUint8Array, transformEDL40Status, tryParseOCMFBonnTariffText, unquotePCDFText, validatePCDFFields, verifyEDL40Document, verifyJSONMessageSignatureResults, verifyJSONMessageSignatures, verifyJSONSignature, verifyJSONSignatureResult, verifyPCDFDocument };
|
|
18672
19060
|
//# sourceMappingURL=index.js.map
|
|
18673
19061
|
//# sourceMappingURL=index.js.map
|