@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.
@@ -11057,8 +11057,7 @@ var Alfen = class {
11057
11057
  "connectors": [{
11058
11058
  "type": asString(connector?.["type"]) ?? "",
11059
11059
  "cable": {
11060
- "length": asNumber(connector?.["cableLength"]) ?? 0,
11061
- "looses": asNumber(connector?.["cableLooses"]) ?? 0
11060
+ "length": asNumber(connector?.["cableLength"]) ?? 0
11062
11061
  }
11063
11062
  }],
11064
11063
  "energyMeters": [
@@ -14653,6 +14652,132 @@ function numberToBytesBE(value, length) {
14653
14652
  }
14654
14653
  return bytes;
14655
14654
  }
14655
+ var OCMFBonnTariffParseError = class extends Error {
14656
+ tariffText;
14657
+ constructor(tariffText, message) {
14658
+ super(message);
14659
+ this.name = "OCMFBonnTariffParseError";
14660
+ this.tariffText = tariffText;
14661
+ }
14662
+ };
14663
+ function parseCents(value, tariffText, fieldName) {
14664
+ if (!/^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(value))
14665
+ throw new OCMFBonnTariffParseError(tariffText, `${fieldName} must be a non-negative decimal number`);
14666
+ const parsedValue = Number(value);
14667
+ if (!Number.isFinite(parsedValue))
14668
+ throw new OCMFBonnTariffParseError(tariffText, `${fieldName} is outside the supported numeric range`);
14669
+ return parsedValue;
14670
+ }
14671
+ function parseOCMFBonnTariffText(tariffText) {
14672
+ const fields = tariffText.split(";");
14673
+ const code = fields[0];
14674
+ if (fields[1] !== "EUR")
14675
+ throw new OCMFBonnTariffParseError(tariffText, "currency must be EUR");
14676
+ switch (code) {
14677
+ case "001":
14678
+ if (fields.length !== 6)
14679
+ throw new OCMFBonnTariffParseError(tariffText, "profile 001 must contain six fields");
14680
+ return {
14681
+ raw: tariffText,
14682
+ code,
14683
+ currency: "EUR",
14684
+ startFeeCents: parseCents(fields[2] ?? "", tariffText, "W"),
14685
+ energyFeeCentsPerKWh: parseCents(fields[3] ?? "", tariffText, "X"),
14686
+ blockingFeeCentsPerMinute: parseCents(fields[4] ?? "", tariffText, "Y"),
14687
+ blockingFeeStartMinute: parseCents(fields[5] ?? "", tariffText, "Z")
14688
+ };
14689
+ case "002":
14690
+ if (fields.length !== 5)
14691
+ throw new OCMFBonnTariffParseError(tariffText, "profile 002 must contain five fields");
14692
+ return {
14693
+ raw: tariffText,
14694
+ code,
14695
+ currency: "EUR",
14696
+ startFeeCents: parseCents(fields[2] ?? "", tariffText, "W"),
14697
+ energyFeeCentsPerKWh: parseCents(fields[3] ?? "", tariffText, "X"),
14698
+ blockingFeeCentsPerMinute: parseCents(fields[4] ?? "", tariffText, "Y"),
14699
+ blockingFeeStartsAfterCharging: true
14700
+ };
14701
+ case "003":
14702
+ if (fields.length !== 4)
14703
+ throw new OCMFBonnTariffParseError(tariffText, "profile 003 must contain four fields");
14704
+ return {
14705
+ raw: tariffText,
14706
+ code,
14707
+ currency: "EUR",
14708
+ startFeeCents: parseCents(fields[2] ?? "", tariffText, "W"),
14709
+ timeFeeCentsPerMinute: parseCents(fields[3] ?? "", tariffText, "X")
14710
+ };
14711
+ default:
14712
+ throw new OCMFBonnTariffParseError(tariffText, "unknown Bonn tariff profile");
14713
+ }
14714
+ }
14715
+ function tryParseOCMFBonnTariffText(tariffText) {
14716
+ try {
14717
+ return parseOCMFBonnTariffText(tariffText);
14718
+ } catch (error) {
14719
+ if (error instanceof OCMFBonnTariffParseError)
14720
+ return void 0;
14721
+ throw error;
14722
+ }
14723
+ }
14724
+ function priceComponent(type, price, stepSize) {
14725
+ return {
14726
+ type,
14727
+ price,
14728
+ step_size: stepSize
14729
+ };
14730
+ }
14731
+ function eurosFromCents(cents) {
14732
+ return new Decimal(cents).dividedBy(100);
14733
+ }
14734
+ function eurosPerHourFromCentsPerMinute(cents) {
14735
+ return eurosFromCents(cents).times(60);
14736
+ }
14737
+ function ocmfBonnTariffToChargingTariff(tariff) {
14738
+ const baseComponents = new Array(
14739
+ priceComponent("FLAT", eurosFromCents(tariff.startFeeCents), 1)
14740
+ );
14741
+ const elements = new Array();
14742
+ switch (tariff.code) {
14743
+ case "001":
14744
+ baseComponents.push(priceComponent("ENERGY", eurosFromCents(tariff.energyFeeCentsPerKWh), 1));
14745
+ elements.push(
14746
+ { price_components: baseComponents },
14747
+ {
14748
+ price_components: [
14749
+ priceComponent("PARKING_TIME", eurosPerHourFromCentsPerMinute(tariff.blockingFeeCentsPerMinute), 60)
14750
+ ],
14751
+ restrictions: {
14752
+ min_duration: tariff.blockingFeeStartMinute * 60
14753
+ }
14754
+ }
14755
+ );
14756
+ break;
14757
+ case "002":
14758
+ baseComponents.push(priceComponent("ENERGY", eurosFromCents(tariff.energyFeeCentsPerKWh), 1));
14759
+ elements.push(
14760
+ { price_components: baseComponents },
14761
+ {
14762
+ price_components: [
14763
+ priceComponent("PARKING_TIME", eurosPerHourFromCentsPerMinute(tariff.blockingFeeCentsPerMinute), 60)
14764
+ ]
14765
+ }
14766
+ );
14767
+ break;
14768
+ case "003":
14769
+ baseComponents.push(priceComponent("TIME", eurosPerHourFromCentsPerMinute(tariff.timeFeeCentsPerMinute), 60));
14770
+ elements.push({ price_components: baseComponents });
14771
+ break;
14772
+ }
14773
+ return {
14774
+ "@id": tariff.raw,
14775
+ currency: tariff.currency,
14776
+ elements
14777
+ };
14778
+ }
14779
+
14780
+ // src/OCMF.ts
14656
14781
  var OCMFv1_x = class extends ACrypt {
14657
14782
  curve = new this.chargy.elliptic.ec("p256");
14658
14783
  constructor(chargy) {
@@ -14742,6 +14867,8 @@ var OCMFv1_x = class extends ACrypt {
14742
14867
  return "Reading Current Type";
14743
14868
  case "CL":
14744
14869
  return "Cumulated Loss";
14870
+ case "EI":
14871
+ return "Error Index";
14745
14872
  case "EF":
14746
14873
  return "Error Flags";
14747
14874
  case "ST":
@@ -14949,10 +15076,39 @@ var OCMF = class {
14949
15076
  const identificationType = firstOCMDJSONDocument.payload.IT;
14950
15077
  const identificationData = firstOCMDJSONDocument.payload.ID;
14951
15078
  const tariffText = firstOCMDJSONDocument.payload.TT;
14952
- const controlerFirmwareVersion = firstOCMDJSONDocument.payload.CF;
15079
+ const tariffTextInterpretation = typeof tariffText === "string" ? tryParseOCMFBonnTariffText(tariffText) : void 0;
15080
+ const chargingTariff = typeof tariffText === "string" && tariffText.length > 0 ? tariffTextInterpretation !== void 0 ? ocmfBonnTariffToChargingTariff(tariffTextInterpretation) : { "@id": tariffText } : void 0;
15081
+ const controllerFirmwareVersion = firstOCMDJSONDocument.payload.CF;
14953
15082
  const lossCompensation = firstOCMDJSONDocument.payload.LC;
15083
+ const signedCable = lossCompensation !== void 0 && typeof lossCompensation.LR === "number" && Number.isFinite(lossCompensation.LR) && typeof lossCompensation.LU === "string" && lossCompensation.LU.length > 0 ? {
15084
+ ...typeof lossCompensation.LN === "string" ? { lossCompensation: lossCompensation.LN } : {},
15085
+ ...typeof lossCompensation.LI === "number" ? { lossCompensationId: lossCompensation.LI.toString() } : {},
15086
+ resistance: lossCompensation.LR,
15087
+ resistanceUnit: lossCompensation.LU
15088
+ } : void 0;
14954
15089
  const chargePointIdType = firstOCMDJSONDocument.payload.CT;
14955
15090
  const chargePointId = firstOCMDJSONDocument.payload.CI;
15091
+ let signedChargingStationId;
15092
+ let signedEVSEId;
15093
+ let signedConnectorId;
15094
+ if (typeof chargePointId === "string" && chargePointId.trim().length > 0) {
15095
+ const normalizedChargePointId = chargePointId.trim();
15096
+ switch (chargePointIdType?.toUpperCase()) {
15097
+ case void 0:
15098
+ break;
15099
+ case "EVSEID":
15100
+ signedEVSEId = normalizedChargePointId;
15101
+ break;
15102
+ case "CBIDC": {
15103
+ const cbidcMatch = /^(\S+)\s+(\S+)$/.exec(normalizedChargePointId);
15104
+ if (cbidcMatch?.[1] !== void 0 && cbidcMatch[2] !== void 0) {
15105
+ signedChargingStationId = cbidcMatch[1];
15106
+ signedConnectorId = cbidcMatch[2];
15107
+ }
15108
+ break;
15109
+ }
15110
+ }
15111
+ }
14956
15112
  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,
14957
15113
  // Charge Point and Public Key" section makes the serial-number fields conditionally
14958
15114
  // mandatory. KEBA KCP30 records identify the signing gateway via GS and omit MS.
@@ -14965,7 +15121,7 @@ var OCMF = class {
14965
15121
  // IT is 1..1 in the user-assignment table for transaction records. Some OCMF 1.0
14966
15122
  // implementations omit it when no user is assigned (IS=false, ID empty). We tolerate
14967
15123
  // this vendor compatibility case instead of rejecting otherwise valid signatures.
14968
- isOptionalString(identificationType) && isOptionalString(identificationData) && isOptionalString(tariffText) && isOptionalString(controlerFirmwareVersion) && isOptionalJSONObject(lossCompensation) && isOptionalString(chargePointIdType) && isOptionalString(chargePointId)) {
15124
+ isOptionalString(identificationType) && isOptionalString(identificationData) && isOptionalString(tariffText) && isOptionalString(controllerFirmwareVersion) && isOptionalJSONObject(lossCompensation) && isOptionalString(chargePointIdType) && isOptionalString(chargePointId)) {
14969
15125
  const paginationPrefix = paging.length > 0 ? paging.charAt(0).toLowerCase() : null;
14970
15126
  const transactionType = paginationPrefix === "t" ? "transaction" /* transaction */ : paginationPrefix === "f" ? "fiscal" /* fiscal */ : "undefined" /* undefined */;
14971
15127
  const pagination = paging.length > 1 ? parseNumber(paging.substring(1)) : null;
@@ -14997,11 +15153,22 @@ var OCMF = class {
14997
15153
  "@id": identificationData ?? "?",
14998
15154
  "type": identificationType ?? "?"
14999
15155
  },
15156
+ "chargingTariffs": chargingTariff !== void 0 ? [chargingTariff] : void 0,
15000
15157
  "ocmf": {
15001
15158
  "formatVersion": formatVersion,
15002
15159
  "gatewayInformation": gatewayInformation,
15003
15160
  "gatewaySerial": gatewaySerial,
15004
- "gatewayVersion": gatewayVersion
15161
+ "gatewayVersion": gatewayVersion,
15162
+ "meterVendor": meterVendor,
15163
+ "meterModel": meterModel,
15164
+ "meterSerial": meterSerial,
15165
+ "meterFirmware": meterFirmware,
15166
+ "tariffText": tariffText,
15167
+ "tariffTextInterpretation": tariffTextInterpretation,
15168
+ "controllerFirmwareVersion": controllerFirmwareVersion,
15169
+ "lossCompensation": lossCompensation,
15170
+ "chargePointIdentificationType": chargePointIdType,
15171
+ "chargePointIdentification": chargePointId
15005
15172
  },
15006
15173
  // "chargingStationOperators": [{
15007
15174
  // "chargingPools": [{
@@ -15068,6 +15235,11 @@ var OCMF = class {
15068
15235
  "@context": "https://open.charging.cloud/contexts/SessionSignatureFormats/OCMFv1.0+json",
15069
15236
  "begin": "?",
15070
15237
  "end": "?",
15238
+ "chargingStationId": signedChargingStationId,
15239
+ "EVSEId": signedEVSEId,
15240
+ "ConnectorId": signedConnectorId,
15241
+ "tariffId": chargingTariff?.["@id"],
15242
+ "chargingTariffs": chargingTariff !== void 0 ? [chargingTariff] : void 0,
15071
15243
  "authorizationStart": {
15072
15244
  "@id": identificationData ?? "?",
15073
15245
  "type": identificationType ?? "?",
@@ -15080,19 +15252,50 @@ var OCMF = class {
15080
15252
  }],
15081
15253
  "certainty": 1
15082
15254
  };
15083
- if (ContainerInfos?.chargingStations !== void 0)
15255
+ const resolvedChargingStationId = signedChargingStationId ?? containerChargingStation?.["@id"];
15256
+ if (resolvedChargingStationId !== void 0) {
15257
+ const matchingContainerStation = containerChargingStation?.["@id"] === resolvedChargingStationId ? containerChargingStation : void 0;
15258
+ const resolvedChargingStation = {
15259
+ ...matchingContainerStation ?? { "@id": resolvedChargingStationId },
15260
+ ...controllerFirmwareVersion !== void 0 ? {
15261
+ firmware: {
15262
+ ...matchingContainerStation?.firmware,
15263
+ version: controllerFirmwareVersion
15264
+ }
15265
+ } : {}
15266
+ };
15267
+ CTR.chargingStations = [
15268
+ resolvedChargingStation,
15269
+ ...ContainerInfos?.chargingStations?.filter((station) => station["@id"] !== resolvedChargingStationId) ?? []
15270
+ ];
15271
+ if (CTR.chargingSessions?.[0] !== void 0) {
15272
+ CTR.chargingSessions[0].chargingStationId ??= resolvedChargingStationId;
15273
+ CTR.chargingSessions[0].chargingStation = resolvedChargingStation;
15274
+ }
15275
+ } else if (ContainerInfos?.chargingStations !== void 0)
15084
15276
  CTR.chargingStations = ContainerInfos.chargingStations;
15085
- if (containerChargingStation !== void 0 && CTR.chargingSessions?.[0] !== void 0) {
15086
- CTR.chargingSessions[0].chargingStationId = containerChargingStation["@id"];
15087
- CTR.chargingSessions[0].chargingStation = containerChargingStation;
15088
- }
15089
15277
  if (containerEVSE !== void 0 && CTR.chargingSessions?.[0] !== void 0) {
15090
- CTR.chargingSessions[0].EVSEId = containerEVSE["@id"];
15091
- CTR.chargingSessions[0].EVSE = containerEVSE;
15092
- }
15093
- if (containerConnector !== void 0 && CTR.chargingSessions?.[0] !== void 0) {
15094
- CTR.chargingSessions[0].ConnectorId = containerConnector["@id"];
15095
- CTR.chargingSessions[0].Connector = containerConnector;
15278
+ const chargingSession = CTR.chargingSessions[0];
15279
+ chargingSession.EVSEId ??= containerEVSE["@id"];
15280
+ if (chargingSession.EVSEId === containerEVSE["@id"])
15281
+ chargingSession.EVSE = containerEVSE;
15282
+ }
15283
+ const resolvedConnectorId = signedConnectorId ?? containerConnector?.["@id"];
15284
+ if ((resolvedConnectorId !== void 0 || signedCable !== void 0) && CTR.chargingSessions?.[0] !== void 0) {
15285
+ const matchingContainerConnector = containerConnector?.["@id"] === resolvedConnectorId ? containerConnector : void 0;
15286
+ const resolvedConnector = {
15287
+ ...matchingContainerConnector,
15288
+ ...resolvedConnectorId !== void 0 ? { "@id": resolvedConnectorId } : {},
15289
+ ...signedCable !== void 0 ? {
15290
+ cable: {
15291
+ ...matchingContainerConnector?.cable,
15292
+ ...signedCable
15293
+ }
15294
+ } : {}
15295
+ };
15296
+ const chargingSession = CTR.chargingSessions[0];
15297
+ chargingSession.ConnectorId ??= resolvedConnectorId;
15298
+ chargingSession.Connector = resolvedConnector;
15096
15299
  }
15097
15300
  const measurementsByKey = /* @__PURE__ */ new Map();
15098
15301
  for (const ocmfJSONDocument of OCMFJSONDocuments) {
@@ -15106,12 +15309,13 @@ var OCMF = class {
15106
15309
  const readingUnit = effectiveReading.RU;
15107
15310
  const readingCurrentType = effectiveReading.RT;
15108
15311
  const cumulatedLoss = effectiveReading.CL;
15312
+ const errorIndex = effectiveReading.EI;
15109
15313
  const errorFlags = effectiveReading.EF;
15110
15314
  const status = effectiveReading.ST;
15111
15315
  inheritedReading = effectiveReading;
15112
15316
  if (isMandatoryString(time) && isOptionalString(transaction) && isMandatoryDecimal(readingValue) && // Note: Some vendors use a JSON string here!
15113
15317
  isOptionalString(readingIdentification) && isMandatoryString(readingUnit) && isOptionalString(readingCurrentType) && // chargyLib.isOptionalDecimal (cumulatedLoss) &&
15114
- isOptionalString(errorFlags) && isMandatoryString(status)) {
15318
+ isOptionalNumber(errorIndex) && isOptionalString(errorFlags) && isMandatoryString(status)) {
15115
15319
  const timeSplit = time.split(" ");
15116
15320
  if (timeSplit.length != 2) return {
15117
15321
  status: "InvalidSessionFormat" /* InvalidSessionFormat */,
@@ -15210,6 +15414,7 @@ var OCMF = class {
15210
15414
  // "T" ToDo: Serialize this to a string!
15211
15415
  "pagination": pagination,
15212
15416
  // "9289"
15417
+ "errorIndex": errorIndex,
15213
15418
  "errorFlags": errorFlags,
15214
15419
  // ""
15215
15420
  "cumulatedLoss": cumulatedLoss != null && cumulatedLoss !== 0 ? new Decimal(cumulatedLoss) : void 0,
@@ -15226,10 +15431,8 @@ var OCMF = class {
15226
15431
  }
15227
15432
  }
15228
15433
  }
15229
- if (ContainerInfos?.chargingStations !== void 0)
15230
- CTR.chargingStations = ContainerInfos.chargingStations;
15231
15434
  if (ContainerInfos?.warnings !== void 0)
15232
- CTR.warnings = [...CTR.warnings ?? [], ...ContainerInfos.warnings];
15435
+ CTR.warnings = (CTR.warnings ?? []).concat(ContainerInfos.warnings);
15233
15436
  CTR.status = OCMFJSONDocuments.every((ocmfJSONDocument) => ocmfJSONDocument.validationStatus === "ValidSignature" /* ValidSignature */) ? "ValidSignature" /* ValidSignature */ : "InvalidSignature" /* InvalidSignature */;
15234
15437
  if (CTR.chargingSessions != null && CTR.chargingSessions.length > 0 && CTR.chargingSessions[0]) {
15235
15438
  CTR.begin = CTR.chargingSessions[0].begin;
@@ -15724,6 +15927,11 @@ var OCMF = class {
15724
15927
  }
15725
15928
  if (ocmfJSONDocumentGroup[0]) {
15726
15929
  switch (ocmfJSONDocumentGroup[0].payload.FV) {
15930
+ // FV has cardinality 0..1 in OCMF. All supported 1.x
15931
+ // versions use the same parser, so an omitted version
15932
+ // is parsed as generic OCMF without changing the
15933
+ // signed payload or inventing a concrete version.
15934
+ case void 0:
15727
15935
  case "0.1":
15728
15936
  // OCMF 0.1 SAFE reference data uses a few legacy field names/forms (VI/VV,
15729
15937
  // string based IS values), but the compact signed document structure is close
@@ -16745,6 +16953,184 @@ function readDERInteger(bytes, getOffset, setOffset) {
16745
16953
  return hex;
16746
16954
  }
16747
16955
 
16956
+ // src/PTBContainer.ts
16957
+ var base64RegExp = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
16958
+ var formatVersionRegExp = /^1(?:\.[0-9]+)?$/;
16959
+ var PTB = class {
16960
+ chargy;
16961
+ constructor(chargy) {
16962
+ this.chargy = chargy;
16963
+ }
16964
+ async TryToParsePTBContainer(container) {
16965
+ const validation = this.validateContainer(container);
16966
+ if (!validation.valid)
16967
+ return this.validationError(validation.issues);
16968
+ const ptbContainer = validation.container;
16969
+ const containerInfos = {
16970
+ chargingStations: [{
16971
+ "@id": ptbContainer.chargeboxIdentifier,
16972
+ address: this.normalizeAddress(ptbContainer.address),
16973
+ geoLocation: {
16974
+ lat: ptbContainer.geoLocation.lat,
16975
+ lng: ptbContainer.geoLocation.lng
16976
+ },
16977
+ EVSEs: [{
16978
+ "@id": ptbContainer.chargeboxIdentifier
16979
+ }]
16980
+ }]
16981
+ };
16982
+ return new OCMF(this.chargy).TryToParseOCMFDocuments(
16983
+ [ptbContainer.ocmfBegin, ptbContainer.ocmfEnd],
16984
+ ptbContainer.publicKey,
16985
+ "base64",
16986
+ containerInfos
16987
+ );
16988
+ }
16989
+ validateContainer(container) {
16990
+ const issues = [];
16991
+ if (!isMandatoryJSONObject(container))
16992
+ return {
16993
+ valid: false,
16994
+ issues: [{
16995
+ path: "$",
16996
+ message: "must be an object"
16997
+ }]
16998
+ };
16999
+ this.requireConstantString(container, "format", "ptb", issues);
17000
+ this.requireString(container, "publicKey", issues);
17001
+ this.requireString(container, "chargeboxIdentifier", issues);
17002
+ this.requireString(container, "ocmfBegin", issues);
17003
+ this.requireString(container, "ocmfEnd", issues);
17004
+ const formatVersion = container["formatVersion"];
17005
+ if (formatVersion !== void 0 && (typeof formatVersion !== "string" || !formatVersionRegExp.test(formatVersion))) {
17006
+ issues.push({
17007
+ path: "$.formatVersion",
17008
+ message: "must match ^1(?:\\.[0-9]+)?$"
17009
+ });
17010
+ }
17011
+ const publicKey = container["publicKey"];
17012
+ if (typeof publicKey === "string" && publicKey.length > 0 && !base64RegExp.test(publicKey))
17013
+ issues.push({
17014
+ path: "$.publicKey",
17015
+ message: "must be a base64 encoded string"
17016
+ });
17017
+ for (const propertyName of ["ocmfBegin", "ocmfEnd"]) {
17018
+ const ocmfDocument = container[propertyName];
17019
+ if (typeof ocmfDocument === "string" && (ocmfDocument.length < 10 || !ocmfDocument.startsWith("OCMF|"))) {
17020
+ issues.push({
17021
+ path: "$." + propertyName,
17022
+ message: "must be an unmodified OCMF record beginning with OCMF|"
17023
+ });
17024
+ }
17025
+ }
17026
+ const address = container["address"];
17027
+ if (!isMandatoryJSONObject(address))
17028
+ issues.push({
17029
+ path: "$.address",
17030
+ message: "must be an object"
17031
+ });
17032
+ else
17033
+ this.validateAddress(address, issues);
17034
+ const geoLocation = container["geoLocation"];
17035
+ if (!isMandatoryJSONObject(geoLocation))
17036
+ issues.push({
17037
+ path: "$.geoLocation",
17038
+ message: "must be an object"
17039
+ });
17040
+ else
17041
+ this.validateGeoLocation(geoLocation, issues);
17042
+ if (issues.length > 0)
17043
+ return {
17044
+ valid: false,
17045
+ issues
17046
+ };
17047
+ return {
17048
+ valid: true,
17049
+ container
17050
+ };
17051
+ }
17052
+ validateAddress(address, issues) {
17053
+ this.requireString(address, "street", issues, "$.address");
17054
+ for (const propertyName of ["houseNumber", "zipCode", "postalCode", "town", "city", "country"]) {
17055
+ const propertyValue = address[propertyName];
17056
+ if (propertyValue !== void 0 && typeof propertyValue !== "string")
17057
+ issues.push({
17058
+ path: "$.address." + propertyName,
17059
+ message: "must be a string"
17060
+ });
17061
+ else if ((propertyName === "town" || propertyName === "city") && propertyValue === "")
17062
+ issues.push({
17063
+ path: "$.address." + propertyName,
17064
+ message: "must be a non-empty string"
17065
+ });
17066
+ }
17067
+ const town = address["town"];
17068
+ const city = address["city"];
17069
+ if ((typeof town !== "string" || town.length === 0) && (typeof city !== "string" || city.length === 0)) {
17070
+ issues.push({
17071
+ path: "$.address",
17072
+ message: "must contain a non-empty town or city"
17073
+ });
17074
+ }
17075
+ }
17076
+ validateGeoLocation(geoLocation, issues) {
17077
+ const latitude = geoLocation["lat"];
17078
+ const longitude = geoLocation["lng"];
17079
+ if (typeof latitude !== "number" || !Number.isFinite(latitude) || latitude < -90 || latitude > 90)
17080
+ issues.push({
17081
+ path: "$.geoLocation.lat",
17082
+ message: "must be a number between -90 and 90"
17083
+ });
17084
+ if (typeof longitude !== "number" || !Number.isFinite(longitude) || longitude < -180 || longitude > 180)
17085
+ issues.push({
17086
+ path: "$.geoLocation.lng",
17087
+ message: "must be a number between -180 and 180"
17088
+ });
17089
+ for (const propertyName of Object.keys(geoLocation))
17090
+ if (propertyName !== "lat" && propertyName !== "lng")
17091
+ issues.push({
17092
+ path: "$.geoLocation." + propertyName,
17093
+ message: "is not allowed"
17094
+ });
17095
+ }
17096
+ requireString(json, propertyName, issues, parentPath = "$") {
17097
+ const value = json[propertyName];
17098
+ if (typeof value !== "string" || value.length === 0)
17099
+ issues.push({
17100
+ path: parentPath + "." + propertyName,
17101
+ message: "must be a non-empty string"
17102
+ });
17103
+ }
17104
+ requireConstantString(json, propertyName, expectedValue, issues) {
17105
+ if (json[propertyName] !== expectedValue)
17106
+ issues.push({
17107
+ path: "$." + propertyName,
17108
+ message: "must equal " + expectedValue
17109
+ });
17110
+ }
17111
+ normalizeAddress(address) {
17112
+ return {
17113
+ city: address.city ?? address.town,
17114
+ street: address.street,
17115
+ houseNumber: address.houseNumber,
17116
+ postalCode: address.postalCode ?? address.zipCode,
17117
+ country: address.country
17118
+ };
17119
+ }
17120
+ validationError(issues) {
17121
+ return {
17122
+ format: "ptb",
17123
+ status: "InvalidSessionFormat" /* InvalidSessionFormat */,
17124
+ message: this.chargy.GetMultilanguageText("Invalid PTB OCMF container!"),
17125
+ certainty: 1,
17126
+ issues,
17127
+ errors: issues.map((issue) => CreateError(
17128
+ this.chargy.GetMultilanguageText(issue.path + " " + issue.message)
17129
+ ))
17130
+ };
17131
+ }
17132
+ };
17133
+
16748
17134
  // src/SAFE_XML.ts
16749
17135
  var SAFEXML = class _SAFEXML {
16750
17136
  chargy;
@@ -19977,7 +20363,9 @@ var Chargy = class {
19977
20363
  if (IsAChargeTransparencyLiveLink(JSONContent)) {
19978
20364
  JSONContent.timestamp ??= (/* @__PURE__ */ new Date()).toISOString();
19979
20365
  processedFile.result = JSONContent;
19980
- } else if (isMandatoryString(JSONContext)) {
20366
+ } else if (JSONContent["format"] === "ptb")
20367
+ processedFile.result = await new PTB(this).TryToParsePTBContainer(JSONContent);
20368
+ else if (isMandatoryString(JSONContext)) {
19981
20369
  if (JSONContext.startsWith("https://open.charging.cloud/contexts/CTR+json"))
19982
20370
  processedFile.result = JSONContent;
19983
20371
  else if (JSONContext.startsWith("https://open.charging.cloud/contexts/publicKey+json"))
@@ -20450,6 +20838,6 @@ buffer/index.js:
20450
20838
  *)
20451
20839
  */
20452
20840
 
20453
- 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 };
20841
+ 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 };
20454
20842
  //# sourceMappingURL=index.js.map
20455
20843
  //# sourceMappingURL=index.js.map