@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.
- package/dist/index.js +47 -12
- 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))
|
|
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 (
|
|
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
|
-
|
|
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))
|
|
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 <
|
|
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
|
}
|