@attraccess/plugin-wago 0.1.0-nightly.940.1 → 0.1.0-nightly.941.1

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.
Files changed (2) hide show
  1. package/dist/index.js +47 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -42,6 +42,31 @@ var import_plugins_backend_sdk9 = require("@attraccess/plugins-backend-sdk");
42
42
  var import_node_crypto3 = require("node:crypto");
43
43
  var import_common = require("@nestjs/common");
44
44
 
45
+ // measurement-contract.ts
46
+ var UNITS = {
47
+ ampere: "milliampere",
48
+ volt: "millivolt",
49
+ watt: "milliwatt",
50
+ "watt-hour": "milliwatt-hour",
51
+ percent: "millipercent"
52
+ };
53
+ var MeasurementContractError = class extends Error {
54
+ constructor(code, message) {
55
+ super(message);
56
+ __publicField(this, "code", code);
57
+ }
58
+ };
59
+ function parseMeasurement(value) {
60
+ if (typeof value.channelId !== "string" || !value.channelId.trim() || !Object.values(UNITS).concat(Object.keys(UNITS)).includes(value.unit) || !Number.isSafeInteger(value.value) || !["live", "cumulative"].includes(value.kind))
61
+ throw new MeasurementContractError("invalid_measurement_message", "invalid integer measurement message");
62
+ return {
63
+ channelId: value.channelId,
64
+ unit: value.unit,
65
+ value: value.value,
66
+ kind: value.kind
67
+ };
68
+ }
69
+
45
70
  // backend/protocol.ts
46
71
  var DISCOVERY_ROOT = "attraccess/wago/discovery";
47
72
  var CONFIGURATION_PROTOCOL_VERSION = 1;
@@ -122,38 +147,43 @@ function parseOperationalMessage(prefix, topic, payload) {
122
147
  const root = `${normalizeOperationalPrefix(prefix)}/v${CONFIGURATION_PROTOCOL_VERSION}/controllers/`;
123
148
  if (!topic.startsWith(root)) return null;
124
149
  const [hardwareId, suffix, extra] = topic.slice(root.length).split("/");
125
- if (!hardwareId || extra || !["state", "measurements", "faults", "acknowledgements"].includes(suffix)) return null;
150
+ if (!hardwareId || /[+#]/.test(hardwareId) || extra !== void 0 || !["state", "measurements", "faults", "acknowledgements"].includes(suffix))
151
+ return null;
126
152
  const value = parseObject(payload, "operational message");
127
153
  const timestamp = requiredTimestamp(value.timestamp);
128
154
  const sequence = requiredSequence(value.sequence);
155
+ if (value.streamId !== void 0 && (typeof value.streamId !== "string" || !value.streamId.trim() || value.streamId.length > 128))
156
+ throw new Error("operational streamId is invalid");
157
+ const streamId = value.streamId;
129
158
  if (suffix === "state") {
130
- if (typeof value.connected !== "boolean" || !isNullableInteger(value.revision) || !isNullableString(value.contentHash) || !isBooleanRecord(value.outputs))
159
+ if (typeof value.connected !== "boolean" || !isNullableInteger(value.revision) || !isNullableString(value.contentHash) || !isBooleanRecord(value.outputs) || value.inputs !== void 0 && !isBooleanRecord(value.inputs) || value.readiness !== void 0 && (!value.readiness || typeof value.readiness !== "object" || Array.isArray(value.readiness) || typeof value.readiness.hardwareAvailable !== "boolean"))
131
160
  throw new Error("invalid state message");
132
161
  return {
133
162
  hardwareId,
134
163
  message: {
135
164
  category: "state",
136
165
  timestamp,
166
+ ...streamId !== void 0 ? { streamId } : {},
137
167
  sequence,
138
168
  connected: value.connected,
139
169
  revision: value.revision,
140
170
  contentHash: value.contentHash,
141
- outputs: value.outputs
171
+ outputs: value.outputs,
172
+ ...value.inputs !== void 0 ? { inputs: value.inputs } : {},
173
+ ...value.readiness !== void 0 ? { readiness: { hardwareAvailable: value.readiness.hardwareAvailable } } : {}
142
174
  }
143
175
  };
144
176
  }
145
177
  if (suffix === "measurements") {
146
- if (typeof value.channelId !== "string" || typeof value.unit !== "string" || typeof value.value !== "number" || !Number.isFinite(value.value))
147
- throw new Error("invalid measurement message");
178
+ if (streamId === void 0) throw new Error("operational streamId is required for measurements");
148
179
  return {
149
180
  hardwareId,
150
181
  message: {
151
182
  category: "measurement",
152
183
  timestamp,
184
+ streamId,
153
185
  sequence,
154
- channelId: value.channelId,
155
- unit: value.unit,
156
- value: value.value
186
+ ...parseMeasurement(value)
157
187
  }
158
188
  };
159
189
  }
@@ -165,6 +195,7 @@ function parseOperationalMessage(prefix, topic, payload) {
165
195
  message: {
166
196
  category: "fault",
167
197
  timestamp,
198
+ ...streamId !== void 0 ? { streamId } : {},
168
199
  sequence,
169
200
  channelId: value.channelId,
170
201
  code: value.code,
@@ -179,6 +210,7 @@ function parseOperationalMessage(prefix, topic, payload) {
179
210
  message: {
180
211
  category: "acknowledgement",
181
212
  timestamp,
213
+ ...streamId !== void 0 ? { streamId } : {},
182
214
  sequence,
183
215
  id: value.id,
184
216
  status: value.status,
@@ -215,11 +247,12 @@ function parseObject(payload, label) {
215
247
  return value;
216
248
  }
217
249
  function requiredTimestamp(value) {
218
- if (typeof value !== "string" || Number.isNaN(Date.parse(value))) throw new Error("operational timestamp is invalid");
250
+ if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value) || Number.isNaN(Date.parse(value)) || new Date(value).toISOString() !== value)
251
+ throw new Error("operational timestamp is invalid");
219
252
  return value;
220
253
  }
221
254
  function requiredSequence(value) {
222
- if (!Number.isSafeInteger(value) || value < 0) throw new Error("operational sequence is invalid");
255
+ if (!Number.isSafeInteger(value) || value < 1) throw new Error("operational sequence is invalid");
223
256
  return value;
224
257
  }
225
258
  function isNullableInteger(value) {
@@ -631,10 +664,12 @@ function validateGuard(value, path, capabilities, channelIds, errors) {
631
664
  function validateMeasurement(value, path, capabilities, errors) {
632
665
  if (value === void 0) return;
633
666
  if (!record(value, path, errors)) return;
634
- exactKeys(value, path, ["unit", "scale", "offset"], errors);
635
- enumValue(value.unit, `${path}.unit`, ["ampere", "volt", "watt", "percent"], errors);
667
+ exactKeys(value, path, ["unit", "scale", "offset", "kind"], errors, ["kind"]);
668
+ enumValue(value.unit, `${path}.unit`, ["ampere", "volt", "watt", "watt-hour", "percent"], errors);
636
669
  if (!Number.isFinite(value.scale) || !Number.isFinite(value.offset))
637
670
  errors.push({ path, code: "invalid_measurement", message: "scale and offset must be finite numbers" });
671
+ if (value.kind !== void 0 && !["live", "cumulative"].includes(value.kind))
672
+ errors.push({ path: `${path}.kind`, code: "unsupported_value", message: "kind must be live or cumulative" });
638
673
  if (!capabilities.has("measurement"))
639
674
  errors.push({ path, code: "unsupported_field", message: "measurement requires measurement capability" });
640
675
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attraccess/plugin-wago",
3
- "version": "0.1.0-nightly.940.1",
3
+ "version": "0.1.0-nightly.941.1",
4
4
  "description": "Official Attraccess integration for WAGO CC100 controllers.",
5
5
  "keywords": [
6
6
  "attraccess-plugin",