@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.
@@ -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 controlerFirmwareVersion = firstOCMDJSONDocument.payload.CF;
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(controlerFirmwareVersion) && isOptionalJSONObject(lossCompensation) && isOptionalString(chargePointIdType) && isOptionalString(chargePointId)) {
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
- if (ContainerInfos?.chargingStations !== void 0)
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].EVSEId = containerEVSE["@id"];
13328
- CTR.chargingSessions[0].EVSE = containerEVSE;
13329
- }
13330
- if (containerConnector !== void 0 && CTR.chargingSessions?.[0] !== void 0) {
13331
- CTR.chargingSessions[0].ConnectorId = containerConnector["@id"];
13332
- CTR.chargingSessions[0].Connector = containerConnector;
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 = [...CTR.warnings ?? [], ...ContainerInfos.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 (isMandatoryString(JSONContext)) {
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