@zwave-js/nvmedit 8.9.0-beta.4-pr-3789-c208add

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 (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/bin/nvmedit.js +2 -0
  4. package/build/cli.d.ts +3 -0
  5. package/build/cli.d.ts.map +1 -0
  6. package/build/cli.js +175 -0
  7. package/build/cli.js.map +1 -0
  8. package/build/consts.d.ts +4 -0
  9. package/build/consts.d.ts.map +1 -0
  10. package/build/consts.js +7 -0
  11. package/build/consts.js.map +1 -0
  12. package/build/convert.d.ts +86 -0
  13. package/build/convert.d.ts.map +1 -0
  14. package/build/convert.js +796 -0
  15. package/build/convert.js.map +1 -0
  16. package/build/files/ApplicationCCsFile.d.ts +22 -0
  17. package/build/files/ApplicationCCsFile.d.ts.map +1 -0
  18. package/build/files/ApplicationCCsFile.js +75 -0
  19. package/build/files/ApplicationCCsFile.js.map +1 -0
  20. package/build/files/ApplicationDataFile.d.ts +12 -0
  21. package/build/files/ApplicationDataFile.d.ts.map +1 -0
  22. package/build/files/ApplicationDataFile.js +31 -0
  23. package/build/files/ApplicationDataFile.js.map +1 -0
  24. package/build/files/ApplicationRFConfigFile.d.ts +21 -0
  25. package/build/files/ApplicationRFConfigFile.d.ts.map +1 -0
  26. package/build/files/ApplicationRFConfigFile.js +71 -0
  27. package/build/files/ApplicationRFConfigFile.js.map +1 -0
  28. package/build/files/ApplicationTypeFile.d.ts +24 -0
  29. package/build/files/ApplicationTypeFile.d.ts.map +1 -0
  30. package/build/files/ApplicationTypeFile.js +52 -0
  31. package/build/files/ApplicationTypeFile.js.map +1 -0
  32. package/build/files/ControllerInfoFile.d.ts +44 -0
  33. package/build/files/ControllerInfoFile.d.ts.map +1 -0
  34. package/build/files/ControllerInfoFile.js +136 -0
  35. package/build/files/ControllerInfoFile.js.map +1 -0
  36. package/build/files/NVMFile.d.ts +48 -0
  37. package/build/files/NVMFile.d.ts.map +1 -0
  38. package/build/files/NVMFile.js +129 -0
  39. package/build/files/NVMFile.js.map +1 -0
  40. package/build/files/NodeInfoFiles.d.ts +38 -0
  41. package/build/files/NodeInfoFiles.d.ts.map +1 -0
  42. package/build/files/NodeInfoFiles.js +146 -0
  43. package/build/files/NodeInfoFiles.js.map +1 -0
  44. package/build/files/ProtocolNodeMaskFiles.d.ts +48 -0
  45. package/build/files/ProtocolNodeMaskFiles.d.ts.map +1 -0
  46. package/build/files/ProtocolNodeMaskFiles.js +120 -0
  47. package/build/files/ProtocolNodeMaskFiles.js.map +1 -0
  48. package/build/files/RouteCacheFiles.d.ts +45 -0
  49. package/build/files/RouteCacheFiles.d.ts.map +1 -0
  50. package/build/files/RouteCacheFiles.js +164 -0
  51. package/build/files/RouteCacheFiles.js.map +1 -0
  52. package/build/files/SUCUpdateEntriesFile.d.ts +25 -0
  53. package/build/files/SUCUpdateEntriesFile.d.ts.map +1 -0
  54. package/build/files/SUCUpdateEntriesFile.js +82 -0
  55. package/build/files/SUCUpdateEntriesFile.js.map +1 -0
  56. package/build/files/VersionFiles.d.ts +27 -0
  57. package/build/files/VersionFiles.d.ts.map +1 -0
  58. package/build/files/VersionFiles.js +60 -0
  59. package/build/files/VersionFiles.js.map +1 -0
  60. package/build/files/index.d.ts +13 -0
  61. package/build/files/index.d.ts.map +1 -0
  62. package/build/files/index.js +25 -0
  63. package/build/files/index.js.map +1 -0
  64. package/build/index.d.ts +8 -0
  65. package/build/index.d.ts.map +1 -0
  66. package/build/index.js +22 -0
  67. package/build/index.js.map +1 -0
  68. package/build/nvm3/consts.d.ts +48 -0
  69. package/build/nvm3/consts.d.ts.map +1 -0
  70. package/build/nvm3/consts.js +59 -0
  71. package/build/nvm3/consts.js.map +1 -0
  72. package/build/nvm3/nvm.d.ts +31 -0
  73. package/build/nvm3/nvm.d.ts.map +1 -0
  74. package/build/nvm3/nvm.js +146 -0
  75. package/build/nvm3/nvm.js.map +1 -0
  76. package/build/nvm3/object.d.ts +26 -0
  77. package/build/nvm3/object.d.ts.map +1 -0
  78. package/build/nvm3/object.js +196 -0
  79. package/build/nvm3/object.js.map +1 -0
  80. package/build/nvm3/page.d.ts +26 -0
  81. package/build/nvm3/page.d.ts.map +1 -0
  82. package/build/nvm3/page.js +94 -0
  83. package/build/nvm3/page.js.map +1 -0
  84. package/build/nvm3/utils.d.ts +11 -0
  85. package/build/nvm3/utils.d.ts.map +1 -0
  86. package/build/nvm3/utils.js +103 -0
  87. package/build/nvm3/utils.js.map +1 -0
  88. package/build/nvm500/EntryParsers.d.ts +27 -0
  89. package/build/nvm500/EntryParsers.d.ts.map +1 -0
  90. package/build/nvm500/EntryParsers.js +82 -0
  91. package/build/nvm500/EntryParsers.js.map +1 -0
  92. package/build/nvm500/NVMParser.d.ts +93 -0
  93. package/build/nvm500/NVMParser.d.ts.map +1 -0
  94. package/build/nvm500/NVMParser.js +524 -0
  95. package/build/nvm500/NVMParser.js.map +1 -0
  96. package/build/nvm500/parsers/Bridge_6_6x.d.ts +3 -0
  97. package/build/nvm500/parsers/Bridge_6_6x.d.ts.map +1 -0
  98. package/build/nvm500/parsers/Bridge_6_6x.js +183 -0
  99. package/build/nvm500/parsers/Bridge_6_6x.js.map +1 -0
  100. package/build/nvm500/parsers/Bridge_6_7x.d.ts +3 -0
  101. package/build/nvm500/parsers/Bridge_6_7x.d.ts.map +1 -0
  102. package/build/nvm500/parsers/Bridge_6_7x.js +199 -0
  103. package/build/nvm500/parsers/Bridge_6_7x.js.map +1 -0
  104. package/build/nvm500/parsers/Bridge_6_8x.d.ts +3 -0
  105. package/build/nvm500/parsers/Bridge_6_8x.d.ts.map +1 -0
  106. package/build/nvm500/parsers/Bridge_6_8x.js +226 -0
  107. package/build/nvm500/parsers/Bridge_6_8x.js.map +1 -0
  108. package/build/nvm500/parsers/Static_6_6x.d.ts +3 -0
  109. package/build/nvm500/parsers/Static_6_6x.d.ts.map +1 -0
  110. package/build/nvm500/parsers/Static_6_6x.js +178 -0
  111. package/build/nvm500/parsers/Static_6_6x.js.map +1 -0
  112. package/build/nvm500/parsers/Static_6_7x.d.ts +3 -0
  113. package/build/nvm500/parsers/Static_6_7x.d.ts.map +1 -0
  114. package/build/nvm500/parsers/Static_6_7x.js +194 -0
  115. package/build/nvm500/parsers/Static_6_7x.js.map +1 -0
  116. package/build/nvm500/parsers/Static_6_8x.d.ts +3 -0
  117. package/build/nvm500/parsers/Static_6_8x.d.ts.map +1 -0
  118. package/build/nvm500/parsers/Static_6_8x.js +222 -0
  119. package/build/nvm500/parsers/Static_6_8x.js.map +1 -0
  120. package/build/nvm500/shared.d.ts +53 -0
  121. package/build/nvm500/shared.d.ts.map +1 -0
  122. package/build/nvm500/shared.js +57 -0
  123. package/build/nvm500/shared.js.map +1 -0
  124. package/build/shared.d.ts +1 -0
  125. package/build/shared.d.ts.map +1 -0
  126. package/build/shared.js +2 -0
  127. package/build/shared.js.map +1 -0
  128. package/package.json +61 -0
@@ -0,0 +1,796 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.migrateNVM = exports.json700To500 = exports.json500To700 = exports.jsonToNVM500 = exports.jsonToNVM = exports.nvm500ToJSON = exports.nvmToJSON = exports.jsonToNVMObjects_v1_to_v3 = exports.jsonToNVMObjects_v0 = exports.nvmObjectsToJSON = exports.nodeHasInfo = void 0;
7
+ const core_1 = require("@zwave-js/core");
8
+ const shared_1 = require("@zwave-js/shared");
9
+ const semver_1 = __importDefault(require("semver"));
10
+ const files_1 = require("./files");
11
+ const nvm_1 = require("./nvm3/nvm");
12
+ const utils_1 = require("./nvm3/utils");
13
+ const NVMParser_1 = require("./nvm500/NVMParser");
14
+ function nodeHasInfo(node) {
15
+ return !node.isVirtual || Object.keys(node).length > 1;
16
+ }
17
+ exports.nodeHasInfo = nodeHasInfo;
18
+ function createEmptyPhysicalNode() {
19
+ return {
20
+ isVirtual: false,
21
+ isListening: false,
22
+ isFrequentListening: false,
23
+ isRouting: false,
24
+ supportedDataRates: [],
25
+ protocolVersion: 0,
26
+ optionalFunctionality: false,
27
+ nodeType: core_1.NodeType["Routing End Node"],
28
+ supportsSecurity: false,
29
+ supportsBeaming: false,
30
+ genericDeviceClass: 0,
31
+ specificDeviceClass: null,
32
+ neighbors: [],
33
+ sucUpdateIndex: 0,
34
+ appRouteLock: false,
35
+ routeSlaveSUC: false,
36
+ sucPendingUpdate: false,
37
+ pendingDiscovery: false,
38
+ lwr: null,
39
+ nlwr: null,
40
+ };
41
+ }
42
+ /** Converts a compressed set of NVM objects to a JSON representation */
43
+ function nvmObjectsToJSON(applicationObjects, protocolObjects) {
44
+ var _a, _b, _c, _d;
45
+ const nodes = new Map();
46
+ const getNode = (id) => {
47
+ if (!nodes.has(id))
48
+ nodes.set(id, createEmptyPhysicalNode());
49
+ return nodes.get(id);
50
+ };
51
+ const getObject = (id) => {
52
+ var _a;
53
+ if (typeof id === "number") {
54
+ return (_a = protocolObjects.get(id)) !== null && _a !== void 0 ? _a : applicationObjects.get(id);
55
+ }
56
+ else {
57
+ for (const [key, obj] of protocolObjects) {
58
+ if (id(key))
59
+ return obj;
60
+ }
61
+ for (const [key, obj] of applicationObjects) {
62
+ if (id(key))
63
+ return obj;
64
+ }
65
+ }
66
+ };
67
+ const getObjectOrThrow = (id) => {
68
+ const ret = getObject(id);
69
+ if (ret)
70
+ return ret;
71
+ throw new Error(`Object not found`);
72
+ };
73
+ const getFileOrThrow = (id) => {
74
+ const obj = getObjectOrThrow(id);
75
+ return files_1.NVMFile.from(obj);
76
+ };
77
+ const getFile = (id) => {
78
+ const obj = getObject(id);
79
+ if (!obj)
80
+ return undefined;
81
+ return files_1.NVMFile.from(obj);
82
+ };
83
+ // Figure out how to parse the individual files
84
+ const protocolVersionFile = getFileOrThrow(files_1.ProtocolVersionFileID);
85
+ const protocolFileFormat = protocolVersionFile.format;
86
+ // Figure out which nodes exist
87
+ const nodeIds = getFileOrThrow(files_1.ProtocolNodeListFileID).nodeIds;
88
+ // Read all flags for all nodes
89
+ const appRouteLock = new Set(getFileOrThrow(files_1.ProtocolAppRouteLockNodeMaskFileID).nodeIds);
90
+ const routeSlaveSUC = new Set(getFileOrThrow(files_1.ProtocolRouteSlaveSUCNodeMaskFileID).nodeIds);
91
+ const sucPendingUpdate = new Set(getFileOrThrow(files_1.ProtocolSUCPendingUpdateNodeMaskFileID).nodeIds);
92
+ const isVirtual = new Set(getFileOrThrow(files_1.ProtocolVirtualNodeMaskFileID).nodeIds);
93
+ const pendingDiscovery = new Set(getFileOrThrow(files_1.ProtocolPendingDiscoveryNodeMaskFileID).nodeIds);
94
+ const routeCacheExists = new Set(getFileOrThrow(files_1.ProtocolRouteCacheExistsNodeMaskFileID).nodeIds);
95
+ // And create each node entry, including virtual ones
96
+ for (const id of nodeIds) {
97
+ const node = getNode(id);
98
+ const rememberOnlyVirtual = () => {
99
+ nodes.set(id, {
100
+ isVirtual: true,
101
+ });
102
+ };
103
+ // Find node info
104
+ let nodeInfo;
105
+ try {
106
+ if (protocolFileFormat === 0) {
107
+ const fileId = (0, files_1.nodeIdToNodeInfoFileIDV0)(id);
108
+ const file = getFileOrThrow(fileId);
109
+ nodeInfo = file.nodeInfo;
110
+ }
111
+ else if (protocolFileFormat <= 3) {
112
+ const fileId = (0, files_1.nodeIdToNodeInfoFileIDV1)(id);
113
+ const file = getFileOrThrow(fileId);
114
+ nodeInfo = file.nodeInfos.find((i) => i.nodeId === id);
115
+ }
116
+ else {
117
+ throw new Error(`Unsupported protocol file format: ${protocolFileFormat}`);
118
+ }
119
+ }
120
+ catch (e) {
121
+ if (e.message.includes("Object not found")) {
122
+ rememberOnlyVirtual();
123
+ continue;
124
+ }
125
+ throw e;
126
+ }
127
+ Object.assign(node, nodeInfo);
128
+ // Evaluate flags
129
+ node.isVirtual = isVirtual.has(id);
130
+ node.appRouteLock = appRouteLock.has(id);
131
+ node.routeSlaveSUC = routeSlaveSUC.has(id);
132
+ node.sucPendingUpdate = sucPendingUpdate.has(id);
133
+ node.pendingDiscovery = pendingDiscovery.has(id);
134
+ if (routeCacheExists.has(id)) {
135
+ let routeCache;
136
+ if (protocolFileFormat === 0) {
137
+ const fileId = (0, files_1.nodeIdToRouteCacheFileIDV0)(id);
138
+ const file = getFileOrThrow(fileId);
139
+ routeCache = file.routeCache;
140
+ }
141
+ else if (protocolFileFormat <= 3) {
142
+ const fileId = (0, files_1.nodeIdToRouteCacheFileIDV1)(id);
143
+ const file = getFileOrThrow(fileId);
144
+ routeCache = file.routeCaches.find((i) => i.nodeId === id);
145
+ }
146
+ else {
147
+ throw new Error(`Unsupported protocol file format: ${protocolFileFormat}`);
148
+ }
149
+ node.lwr = routeCache.lwr;
150
+ node.nlwr = routeCache.nlwr;
151
+ }
152
+ // @ts-expect-error Some fields include a nodeId, but we don't need it
153
+ delete node.nodeId;
154
+ }
155
+ // Now read info about the controller
156
+ const controllerInfoFile = getFileOrThrow(files_1.ControllerInfoFileID);
157
+ const sucUpdateEntries = getFileOrThrow(files_1.SUCUpdateEntriesFileID).updateEntries;
158
+ const rfConfigFile = getFile(files_1.ApplicationRFConfigFileID);
159
+ const applicationVersionFile = getFileOrThrow(files_1.ApplicationVersionFileID);
160
+ const applicationCCsFile = getFileOrThrow(files_1.ApplicationCCsFileID);
161
+ const applicationDataFile = getFile(files_1.ApplicationDataFileID);
162
+ const applicationTypeFile = getFileOrThrow(files_1.ApplicationTypeFileID);
163
+ const preferredRepeaters = (_a = getFile(files_1.ProtocolPreferredRepeatersFileID)) === null || _a === void 0 ? void 0 : _a.nodeIds;
164
+ const controllerProps = [
165
+ "nodeId",
166
+ "lastNodeId",
167
+ "staticControllerNodeId",
168
+ "sucLastIndex",
169
+ "controllerConfiguration",
170
+ "sucAwarenessPushNeeded",
171
+ "maxNodeId",
172
+ "reservedId",
173
+ "systemState",
174
+ "lastNodeIdLR",
175
+ "maxNodeIdLR",
176
+ "reservedIdLR",
177
+ "primaryLongRangeChannelId",
178
+ "dcdcConfig",
179
+ ];
180
+ const controller = {
181
+ protocolVersion: `${protocolVersionFile.major}.${protocolVersionFile.minor}.${protocolVersionFile.patch}`,
182
+ applicationVersion: `${applicationVersionFile.major}.${applicationVersionFile.minor}.${applicationVersionFile.patch}`,
183
+ homeId: `0x${controllerInfoFile.homeId.toString("hex")}`,
184
+ ...(0, shared_1.pick)(controllerInfoFile, controllerProps),
185
+ ...(0, shared_1.pick)(applicationTypeFile, [
186
+ "isListening",
187
+ "optionalFunctionality",
188
+ "genericDeviceClass",
189
+ "specificDeviceClass",
190
+ ]),
191
+ commandClasses: (0, shared_1.pick)(applicationCCsFile, [
192
+ "includedInsecurely",
193
+ "includedSecurelyInsecureCCs",
194
+ "includedSecurelySecureCCs",
195
+ ]),
196
+ preferredRepeaters,
197
+ ...(rfConfigFile
198
+ ? {
199
+ rfConfig: {
200
+ rfRegion: rfConfigFile.rfRegion,
201
+ txPower: rfConfigFile.txPower,
202
+ measured0dBm: rfConfigFile.measured0dBm,
203
+ enablePTI: (_b = rfConfigFile.enablePTI) !== null && _b !== void 0 ? _b : null,
204
+ maxTXPower: (_c = rfConfigFile.maxTXPower) !== null && _c !== void 0 ? _c : null,
205
+ },
206
+ }
207
+ : {}),
208
+ sucUpdateEntries,
209
+ applicationData: (_d = applicationDataFile === null || applicationDataFile === void 0 ? void 0 : applicationDataFile.data.toString("hex")) !== null && _d !== void 0 ? _d : null,
210
+ };
211
+ // Make sure all props are defined
212
+ const optionalControllerProps = [
213
+ "sucAwarenessPushNeeded",
214
+ "lastNodeIdLR",
215
+ "maxNodeIdLR",
216
+ "reservedIdLR",
217
+ "primaryLongRangeChannelId",
218
+ "dcdcConfig",
219
+ "rfConfig",
220
+ "preferredRepeaters",
221
+ "applicationData",
222
+ ];
223
+ for (const prop of optionalControllerProps) {
224
+ if (controller[prop] === undefined)
225
+ controller[prop] = null;
226
+ }
227
+ const ret = {
228
+ format: protocolFileFormat,
229
+ controller,
230
+ nodes: (0, utils_1.mapToObject)(nodes),
231
+ };
232
+ return ret;
233
+ }
234
+ exports.nvmObjectsToJSON = nvmObjectsToJSON;
235
+ function nvmJSONNodeToNodeInfo(nodeId, node) {
236
+ return {
237
+ nodeId,
238
+ ...(0, shared_1.pick)(node, [
239
+ "isListening",
240
+ "isFrequentListening",
241
+ "isRouting",
242
+ "supportedDataRates",
243
+ "protocolVersion",
244
+ "optionalFunctionality",
245
+ "nodeType",
246
+ "supportsSecurity",
247
+ "supportsBeaming",
248
+ "genericDeviceClass",
249
+ "specificDeviceClass",
250
+ "neighbors",
251
+ "sucUpdateIndex",
252
+ ]),
253
+ };
254
+ }
255
+ function nvmJSONControllerToFileOptions(ctrlr) {
256
+ const ret = {
257
+ homeId: Buffer.from(ctrlr.homeId.replace(/^0x/, ""), "hex"),
258
+ nodeId: ctrlr.nodeId,
259
+ lastNodeId: ctrlr.lastNodeId,
260
+ staticControllerNodeId: ctrlr.staticControllerNodeId,
261
+ sucLastIndex: ctrlr.sucLastIndex,
262
+ controllerConfiguration: ctrlr.controllerConfiguration,
263
+ maxNodeId: ctrlr.maxNodeId,
264
+ reservedId: ctrlr.reservedId,
265
+ systemState: ctrlr.systemState,
266
+ };
267
+ if (ctrlr.sucAwarenessPushNeeded != undefined) {
268
+ // @ts-expect-error We're dealing with a conditional object here
269
+ // TS doesn't like that.
270
+ ret.sucAwarenessPushNeeded = ctrlr.sucAwarenessPushNeeded;
271
+ }
272
+ else {
273
+ Object.assign(ret, (0, core_1.stripUndefined)((0, shared_1.pick)(ctrlr, [
274
+ "sucAwarenessPushNeeded",
275
+ "lastNodeIdLR",
276
+ "maxNodeIdLR",
277
+ "reservedIdLR",
278
+ "primaryLongRangeChannelId",
279
+ "dcdcConfig",
280
+ ])));
281
+ }
282
+ return ret;
283
+ }
284
+ function serializeCommonApplicationObjects(nvm) {
285
+ var _a, _b;
286
+ const ret = [];
287
+ const applTypeFile = new files_1.ApplicationTypeFile((0, shared_1.pick)(nvm.controller, [
288
+ "isListening",
289
+ "optionalFunctionality",
290
+ "genericDeviceClass",
291
+ "specificDeviceClass",
292
+ ]));
293
+ ret.push(applTypeFile.serialize());
294
+ const applCCsFile = new files_1.ApplicationCCsFile((0, shared_1.pick)(nvm.controller.commandClasses, [
295
+ "includedInsecurely",
296
+ "includedSecurelyInsecureCCs",
297
+ "includedSecurelySecureCCs",
298
+ ]));
299
+ ret.push(applCCsFile.serialize());
300
+ if (nvm.controller.rfConfig) {
301
+ const applRFConfigFile = new files_1.ApplicationRFConfigFile({
302
+ ...(0, shared_1.pick)(nvm.controller.rfConfig, [
303
+ "rfRegion",
304
+ "txPower",
305
+ "measured0dBm",
306
+ ]),
307
+ enablePTI: (_a = nvm.controller.rfConfig.enablePTI) !== null && _a !== void 0 ? _a : undefined,
308
+ maxTXPower: (_b = nvm.controller.rfConfig.maxTXPower) !== null && _b !== void 0 ? _b : undefined,
309
+ });
310
+ ret.push(applRFConfigFile.serialize());
311
+ }
312
+ if (nvm.controller.applicationData) {
313
+ // TODO: ensure this is 512 bytes long
314
+ const applDataFile = new files_1.ApplicationDataFile({
315
+ data: Buffer.from(nvm.controller.applicationData, "hex"),
316
+ });
317
+ ret.push(applDataFile.serialize());
318
+ }
319
+ return ret;
320
+ }
321
+ function serializeCommonProtocolObjects(nvm) {
322
+ var _a;
323
+ const ret = [];
324
+ const appRouteLock = new Set();
325
+ const routeSlaveSUC = new Set();
326
+ const sucPendingUpdate = new Set();
327
+ const isVirtual = new Set();
328
+ const pendingDiscovery = new Set();
329
+ for (const [id, node] of Object.entries(nvm.nodes)) {
330
+ const nodeId = parseInt(id);
331
+ if (!nodeHasInfo(node)) {
332
+ isVirtual.add(nodeId);
333
+ continue;
334
+ }
335
+ else {
336
+ if (node.isVirtual)
337
+ isVirtual.add(nodeId);
338
+ if (node.appRouteLock)
339
+ appRouteLock.add(nodeId);
340
+ if (node.routeSlaveSUC)
341
+ routeSlaveSUC.add(nodeId);
342
+ if (node.sucPendingUpdate)
343
+ sucPendingUpdate.add(nodeId);
344
+ if (node.pendingDiscovery)
345
+ pendingDiscovery.add(nodeId);
346
+ }
347
+ }
348
+ ret.push(new files_1.ControllerInfoFile(nvmJSONControllerToFileOptions(nvm.controller)).serialize());
349
+ ret.push(new files_1.ProtocolAppRouteLockNodeMaskFile({
350
+ nodeIds: [...appRouteLock],
351
+ }).serialize());
352
+ ret.push(new files_1.ProtocolRouteSlaveSUCNodeMaskFile({
353
+ nodeIds: [...routeSlaveSUC],
354
+ }).serialize());
355
+ ret.push(new files_1.ProtocolSUCPendingUpdateNodeMaskFile({
356
+ nodeIds: [...sucPendingUpdate],
357
+ }).serialize());
358
+ ret.push(new files_1.ProtocolVirtualNodeMaskFile({
359
+ nodeIds: [...isVirtual],
360
+ }).serialize());
361
+ ret.push(new files_1.ProtocolPendingDiscoveryNodeMaskFile({
362
+ nodeIds: [...pendingDiscovery],
363
+ }).serialize());
364
+ // TODO: format >= 2: { .key = FILE_ID_LRANGE_NODE_EXIST, .size = FILE_SIZE_LRANGE_NODE_EXIST, .name = "LRANGE_NODE_EXIST"},
365
+ if ((_a = nvm.controller.preferredRepeaters) === null || _a === void 0 ? void 0 : _a.length) {
366
+ ret.push(new files_1.ProtocolPreferredRepeatersFile({
367
+ nodeIds: nvm.controller.preferredRepeaters,
368
+ }).serialize());
369
+ }
370
+ ret.push(new files_1.SUCUpdateEntriesFile({
371
+ updateEntries: nvm.controller.sucUpdateEntries,
372
+ }).serialize());
373
+ return ret;
374
+ }
375
+ function jsonToNVMObjects_v0(json, major, minor, patch) {
376
+ var _a, _b;
377
+ const target = (0, shared_1.cloneDeep)(json);
378
+ target.format = 0;
379
+ const applicationObjects = new Map();
380
+ const protocolObjects = new Map();
381
+ const addApplicationObjects = (...objects) => {
382
+ for (const o of objects) {
383
+ applicationObjects.set(o.key, o);
384
+ }
385
+ };
386
+ const addProtocolObjects = (...objects) => {
387
+ for (const o of objects) {
388
+ protocolObjects.set(o.key, o);
389
+ }
390
+ };
391
+ // Application files
392
+ const [applMajor, applMinor, applPatch] = target.controller.applicationVersion.split(".").map((i) => parseInt(i));
393
+ const applVersionFile = new files_1.ApplicationVersionFile({
394
+ format: target.format,
395
+ major: applMajor,
396
+ minor: applMinor,
397
+ patch: applPatch,
398
+ });
399
+ addApplicationObjects(applVersionFile.serialize());
400
+ addApplicationObjects(...serializeCommonApplicationObjects(target));
401
+ // Protocol files
402
+ const protocolVersionFile = new files_1.ProtocolVersionFile({
403
+ format: target.format,
404
+ major,
405
+ minor,
406
+ patch,
407
+ });
408
+ addProtocolObjects(protocolVersionFile.serialize());
409
+ const nodeInfoFiles = new Map();
410
+ const routeCacheFiles = new Map();
411
+ const nodeInfoExists = new Set();
412
+ const routeCacheExists = new Set();
413
+ for (const [id, node] of Object.entries(target.nodes)) {
414
+ const nodeId = parseInt(id);
415
+ if (!nodeHasInfo(node))
416
+ continue;
417
+ nodeInfoExists.add(nodeId);
418
+ // Create/update node info file
419
+ const nodeInfoFileIndex = (0, files_1.nodeIdToNodeInfoFileIDV0)(nodeId);
420
+ nodeInfoFiles.set(nodeInfoFileIndex, new files_1.NodeInfoFileV0({
421
+ nodeInfo: nvmJSONNodeToNodeInfo(nodeId, node),
422
+ }));
423
+ // Create/update route cache file (if there is a route)
424
+ if (node.lwr || node.nlwr) {
425
+ routeCacheExists.add(nodeId);
426
+ const routeCacheFileIndex = (0, files_1.nodeIdToRouteCacheFileIDV0)(nodeId);
427
+ routeCacheFiles.set(routeCacheFileIndex, new files_1.RouteCacheFileV0({
428
+ routeCache: {
429
+ nodeId,
430
+ lwr: (_a = node.lwr) !== null && _a !== void 0 ? _a : (0, files_1.getEmptyRoute)(),
431
+ nlwr: (_b = node.nlwr) !== null && _b !== void 0 ? _b : (0, files_1.getEmptyRoute)(),
432
+ },
433
+ }));
434
+ }
435
+ }
436
+ addProtocolObjects(...serializeCommonProtocolObjects(target));
437
+ addProtocolObjects(new files_1.ProtocolNodeListFile({ nodeIds: [...nodeInfoExists] }).serialize());
438
+ addProtocolObjects(new files_1.ProtocolRouteCacheExistsNodeMaskFile({
439
+ nodeIds: [...routeCacheExists],
440
+ }).serialize());
441
+ if (nodeInfoFiles.size > 0) {
442
+ addProtocolObjects(...[...nodeInfoFiles.values()].map((f) => f.serialize()));
443
+ }
444
+ if (routeCacheFiles.size > 0) {
445
+ addProtocolObjects(...[...routeCacheFiles.values()].map((f) => f.serialize()));
446
+ }
447
+ return {
448
+ applicationObjects,
449
+ protocolObjects,
450
+ };
451
+ }
452
+ exports.jsonToNVMObjects_v0 = jsonToNVMObjects_v0;
453
+ function jsonToNVMObjects_v1_to_v3(format, json, major, minor, patch) {
454
+ var _a, _b;
455
+ const target = (0, shared_1.cloneDeep)(json);
456
+ target.format = format;
457
+ const applicationObjects = new Map();
458
+ const protocolObjects = new Map();
459
+ const addApplicationObjects = (...objects) => {
460
+ for (const o of objects) {
461
+ applicationObjects.set(o.key, o);
462
+ }
463
+ };
464
+ const addProtocolObjects = (...objects) => {
465
+ for (const o of objects) {
466
+ protocolObjects.set(o.key, o);
467
+ }
468
+ };
469
+ // Application files
470
+ const [applMajor, applMinor, applPatch] = target.controller.applicationVersion.split(".").map((i) => parseInt(i));
471
+ const applVersionFile = new files_1.ApplicationVersionFile({
472
+ format: target.format,
473
+ major: applMajor,
474
+ minor: applMinor,
475
+ patch: applPatch,
476
+ });
477
+ addApplicationObjects(applVersionFile.serialize());
478
+ addApplicationObjects(...serializeCommonApplicationObjects(target));
479
+ // Protocol files
480
+ const protocolVersionFile = new files_1.ProtocolVersionFile({
481
+ format: target.format,
482
+ major,
483
+ minor,
484
+ patch,
485
+ });
486
+ addProtocolObjects(protocolVersionFile.serialize());
487
+ const nodeInfoFiles = new Map();
488
+ const routeCacheFiles = new Map();
489
+ const nodeInfoExists = new Set();
490
+ const routeCacheExists = new Set();
491
+ for (const [id, node] of Object.entries(target.nodes)) {
492
+ const nodeId = parseInt(id);
493
+ if (!nodeHasInfo(node))
494
+ continue;
495
+ nodeInfoExists.add(nodeId);
496
+ // Create/update node info file
497
+ const nodeInfoFileIndex = (0, files_1.nodeIdToNodeInfoFileIDV1)(nodeId);
498
+ if (!nodeInfoFiles.has(nodeInfoFileIndex)) {
499
+ nodeInfoFiles.set(nodeInfoFileIndex, new files_1.NodeInfoFileV1({
500
+ nodeInfos: [],
501
+ }));
502
+ }
503
+ const nodeInfoFile = nodeInfoFiles.get(nodeInfoFileIndex);
504
+ nodeInfoFile.nodeInfos.push(nvmJSONNodeToNodeInfo(nodeId, node));
505
+ // Create/update route cache file (if there is a route)
506
+ if (node.lwr || node.nlwr) {
507
+ routeCacheExists.add(nodeId);
508
+ const routeCacheFileIndex = (0, files_1.nodeIdToRouteCacheFileIDV1)(nodeId);
509
+ if (!routeCacheFiles.has(routeCacheFileIndex)) {
510
+ routeCacheFiles.set(routeCacheFileIndex, new files_1.RouteCacheFileV1({
511
+ routeCaches: [],
512
+ }));
513
+ }
514
+ const routeCacheFile = routeCacheFiles.get(routeCacheFileIndex);
515
+ routeCacheFile.routeCaches.push({
516
+ nodeId,
517
+ lwr: (_a = node.lwr) !== null && _a !== void 0 ? _a : (0, files_1.getEmptyRoute)(),
518
+ nlwr: (_b = node.nlwr) !== null && _b !== void 0 ? _b : (0, files_1.getEmptyRoute)(),
519
+ });
520
+ }
521
+ }
522
+ addProtocolObjects(...serializeCommonProtocolObjects(target));
523
+ addProtocolObjects(new files_1.ProtocolNodeListFile({ nodeIds: [...nodeInfoExists] }).serialize());
524
+ addProtocolObjects(new files_1.ProtocolRouteCacheExistsNodeMaskFile({
525
+ nodeIds: [...routeCacheExists],
526
+ }).serialize());
527
+ if (nodeInfoFiles.size > 0) {
528
+ addProtocolObjects(...[...nodeInfoFiles.values()].map((f) => f.serialize()));
529
+ }
530
+ if (routeCacheFiles.size > 0) {
531
+ addProtocolObjects(...[...routeCacheFiles.values()].map((f) => f.serialize()));
532
+ }
533
+ return {
534
+ applicationObjects,
535
+ protocolObjects,
536
+ };
537
+ }
538
+ exports.jsonToNVMObjects_v1_to_v3 = jsonToNVMObjects_v1_to_v3;
539
+ /** Reads an NVM buffer and returns its JSON representation */
540
+ function nvmToJSON(buffer, debugLogs = false) {
541
+ const nvm = (0, nvm_1.parseNVM)(buffer, debugLogs);
542
+ const ret = nvmObjectsToJSON(nvm.applicationObjects, nvm.protocolObjects);
543
+ ret.meta = (0, nvm_1.getNVMMeta)(nvm.protocolPages[0]);
544
+ return ret;
545
+ }
546
+ exports.nvmToJSON = nvmToJSON;
547
+ /** Reads an NVM buffer of a 500-series stick and returns its JSON representation */
548
+ function nvm500ToJSON(buffer) {
549
+ const parser = (0, NVMParser_1.createParser)(buffer);
550
+ if (!parser)
551
+ throw new Error("Unsupported NVM format!");
552
+ return parser.toJSON();
553
+ }
554
+ exports.nvm500ToJSON = nvm500ToJSON;
555
+ /** Takes a JSON represented NVM and converts it to binary */
556
+ function jsonToNVM(json, protocolVersion) {
557
+ const parsedVersion = semver_1.default.parse(protocolVersion);
558
+ if (!parsedVersion) {
559
+ throw new Error(`Invalid protocol version: ${protocolVersion}`);
560
+ }
561
+ let objects;
562
+ if (semver_1.default.gte(parsedVersion, "7.15.3")) {
563
+ objects = jsonToNVMObjects_v1_to_v3(3, json, parsedVersion.major, parsedVersion.minor, parsedVersion.patch);
564
+ }
565
+ else if (semver_1.default.gte(parsedVersion, "7.12.0")) {
566
+ objects = jsonToNVMObjects_v1_to_v3(2, json, parsedVersion.major, parsedVersion.minor, parsedVersion.patch);
567
+ }
568
+ else if (semver_1.default.gte(parsedVersion, "7.11.0")) {
569
+ objects = jsonToNVMObjects_v1_to_v3(1, json, parsedVersion.major, parsedVersion.minor, parsedVersion.patch);
570
+ }
571
+ else if (semver_1.default.gte(parsedVersion, "7.0.0")) {
572
+ objects = jsonToNVMObjects_v0(json, parsedVersion.major, parsedVersion.minor, parsedVersion.patch);
573
+ }
574
+ else {
575
+ throw new Error("jsonToNVM cannot convert to a pre-7.0 NVM version");
576
+ }
577
+ return (0, nvm_1.encodeNVM)(objects.applicationObjects, objects.protocolObjects, json.meta);
578
+ }
579
+ exports.jsonToNVM = jsonToNVM;
580
+ /** Takes a JSON represented 500 series NVM and converts it to binary */
581
+ function jsonToNVM500(json, protocolVersion) {
582
+ // Try to find a matching implementation
583
+ const impl = NVMParser_1.nmvDetails500.find((p) => p.protocolVersions.includes(protocolVersion) &&
584
+ p.name.toLowerCase().startsWith(json.meta.library));
585
+ if (!impl) {
586
+ throw new Error(`Did not find a matching implementation for protocol version ${protocolVersion} and library ${json.meta.library}.`);
587
+ }
588
+ const serializer = new NVMParser_1.NVMSerializer(impl);
589
+ serializer.parseJSON(json, protocolVersion);
590
+ return serializer.serialize();
591
+ }
592
+ exports.jsonToNVM500 = jsonToNVM500;
593
+ function json500To700(json, truncateApplicationData) {
594
+ var _a;
595
+ const source = (0, shared_1.cloneDeep)(json);
596
+ // On the 500 series, some properties are only defined for the nodes, so we pull it off the
597
+ // controller's node entry
598
+ let controllerNode = source.nodes[source.controller.nodeId || 1]; // Little hack because TS doesn't type check the union type properly
599
+ if (!nodeHasInfo(controllerNode)) {
600
+ // No information available, use sensible defaults
601
+ controllerNode = {
602
+ isListening: true,
603
+ optionalFunctionality: false,
604
+ // Static PC Controller
605
+ genericDeviceClass: 0x02,
606
+ specificDeviceClass: 0x01,
607
+ };
608
+ }
609
+ let applicationData = null;
610
+ if (source.controller.applicationData) {
611
+ let raw = Buffer.from(source.controller.applicationData, "hex");
612
+ // Find actual start and end of application data, ignoring zeroes
613
+ let start = 0;
614
+ while (start < raw.length && raw[start] === 0) {
615
+ start++;
616
+ }
617
+ let end = raw.length - 1;
618
+ while (end > start && raw[end] === 0) {
619
+ end--;
620
+ }
621
+ raw = raw.slice(start, end + 1);
622
+ if (raw.length > 512) {
623
+ if (!truncateApplicationData) {
624
+ throw new Error("Invalid NVM JSON: Application data would be truncated! Set truncateApplicationData to true to allow this.");
625
+ }
626
+ raw = raw.slice(0, 512);
627
+ }
628
+ applicationData = raw.toString("hex");
629
+ }
630
+ let homeId;
631
+ if (source.controller.controllerConfiguration &
632
+ core_1.ControllerCapabilityFlags.OnOtherNetwork) {
633
+ // The controller did not start the network itself
634
+ if (!source.controller.learnedHomeId) {
635
+ throw new Error("Invalid NVM JSON: Controller is part of another network but has no learned Home ID!");
636
+ }
637
+ else if (!source.controller.nodeId) {
638
+ throw new Error("Invalid NVM JSON: Controller is part of another network but node ID is zero!");
639
+ }
640
+ homeId = source.controller.learnedHomeId;
641
+ }
642
+ else {
643
+ // The controller did start the network itself
644
+ homeId = source.controller.ownHomeId;
645
+ // it is safe to set the node ID to 1
646
+ source.controller.nodeId = 1;
647
+ }
648
+ const ret = {
649
+ // Start out with format 0 (= protocol version 7.0.0), the jsonToNVM routines will do further conversion
650
+ format: 0,
651
+ controller: {
652
+ // This will contain the original 6.x protocol version, but the jsonToNVM routines will update it
653
+ protocolVersion: source.controller.protocolVersion,
654
+ applicationVersion: source.controller.applicationVersion,
655
+ homeId,
656
+ nodeId: source.controller.nodeId,
657
+ lastNodeId: source.controller.lastNodeId,
658
+ staticControllerNodeId: source.controller.staticControllerNodeId,
659
+ sucLastIndex: source.controller.sucLastIndex,
660
+ controllerConfiguration: source.controller.controllerConfiguration,
661
+ sucUpdateEntries: source.controller.sucUpdateEntries,
662
+ maxNodeId: source.controller.maxNodeId,
663
+ reservedId: source.controller.reservedId,
664
+ systemState: source.controller.systemState,
665
+ preferredRepeaters: source.controller.preferredRepeaters,
666
+ // RF config exists on both series but isn't compatible
667
+ isListening: controllerNode.isListening,
668
+ optionalFunctionality: controllerNode.optionalFunctionality,
669
+ genericDeviceClass: controllerNode.genericDeviceClass,
670
+ specificDeviceClass: (_a = controllerNode.specificDeviceClass) !== null && _a !== void 0 ? _a : 0,
671
+ commandClasses: {
672
+ includedInsecurely: source.controller.commandClasses,
673
+ includedSecurelyInsecureCCs: [],
674
+ includedSecurelySecureCCs: [],
675
+ },
676
+ applicationData,
677
+ },
678
+ // The node entries are actually compatible between the two JSON versions
679
+ // but the types are structured differently
680
+ nodes: source.nodes,
681
+ };
682
+ return ret;
683
+ }
684
+ exports.json500To700 = json500To700;
685
+ function json700To500(json) {
686
+ var _a;
687
+ const source = (0, shared_1.cloneDeep)(json);
688
+ const ret = {
689
+ format: 500,
690
+ controller: {
691
+ // This will contain the original 7.x protocol version, but the jsonToNVM routines will update it
692
+ protocolVersion: source.controller.protocolVersion,
693
+ applicationVersion: source.controller.applicationVersion,
694
+ // The 700 series does not distinguish between own and learned home ID in NVM
695
+ // We infer it from the controller configuration if we need it
696
+ ownHomeId: source.controller.homeId,
697
+ learnedHomeId: source.controller.controllerConfiguration &
698
+ core_1.ControllerCapabilityFlags.OnOtherNetwork
699
+ ? source.controller.homeId
700
+ : null,
701
+ nodeId: source.controller.nodeId,
702
+ lastNodeId: source.controller.lastNodeId,
703
+ staticControllerNodeId: source.controller.staticControllerNodeId,
704
+ sucLastIndex: source.controller.sucLastIndex,
705
+ controllerConfiguration: source.controller.controllerConfiguration,
706
+ sucUpdateEntries: source.controller.sucUpdateEntries,
707
+ maxNodeId: source.controller.maxNodeId,
708
+ reservedId: source.controller.reservedId,
709
+ systemState: source.controller.systemState,
710
+ watchdogStarted: 0,
711
+ preferredRepeaters: (_a = json.controller.preferredRepeaters) !== null && _a !== void 0 ? _a : [],
712
+ // RF config exists on both series but isn't compatible. So set the default
713
+ rfConfig: {
714
+ powerLevelNormal: [255, 255, 255],
715
+ powerLevelLow: [255, 255, 255],
716
+ powerMode: 255,
717
+ powerModeExtintEnable: 255,
718
+ powerModeWutTimeout: 0xffffffff,
719
+ },
720
+ commandClasses: source.controller.commandClasses.includedInsecurely,
721
+ applicationData: source.controller.applicationData,
722
+ },
723
+ // The node entries are actually compatible between the two JSON versions
724
+ // just the types are structured differently
725
+ nodes: source.nodes,
726
+ };
727
+ return ret;
728
+ }
729
+ exports.json700To500 = json700To500;
730
+ /** Converts the given source NVM into a format that is compatible with the given target NVM */
731
+ function migrateNVM(source, target) {
732
+ let sourceJSON;
733
+ let targetJSON;
734
+ let sourceIs500;
735
+ let targetIs500;
736
+ try {
737
+ sourceJSON = nvmToJSON(source);
738
+ sourceIs500 = false;
739
+ }
740
+ catch (e) {
741
+ try {
742
+ sourceJSON = nvm500ToJSON(source);
743
+ sourceIs500 = true;
744
+ }
745
+ catch (ee) {
746
+ throw new Error("Could not parse source NVM - invalid format!");
747
+ }
748
+ }
749
+ try {
750
+ targetJSON = nvmToJSON(target);
751
+ targetIs500 = false;
752
+ }
753
+ catch (e) {
754
+ try {
755
+ targetJSON = nvm500ToJSON(target);
756
+ targetIs500 = true;
757
+ }
758
+ catch (ee) {
759
+ throw new Error("Could not parse target NVM - invalid format!");
760
+ }
761
+ }
762
+ if (sourceIs500 && targetIs500) {
763
+ // Both are 500, so we just need to update the metadata to match the target
764
+ const json = {
765
+ ...sourceJSON,
766
+ meta: targetJSON.meta,
767
+ };
768
+ return jsonToNVM500(json, targetJSON.controller.protocolVersion);
769
+ }
770
+ else if (sourceIs500 && !targetIs500) {
771
+ // We need to upgrade the source to 700 series
772
+ const json = {
773
+ ...json500To700(sourceJSON, true),
774
+ meta: targetJSON.meta,
775
+ };
776
+ return jsonToNVM(json, targetJSON.controller.protocolVersion);
777
+ }
778
+ else if (!sourceIs500 && targetIs500) {
779
+ // We need to downgrade the source to 500 series
780
+ const json = {
781
+ ...json700To500(sourceJSON),
782
+ meta: targetJSON.meta,
783
+ };
784
+ return jsonToNVM500(json, targetJSON.controller.protocolVersion);
785
+ }
786
+ else {
787
+ // Both are 700, so we just need to update the metadata to match the target
788
+ const json = {
789
+ ...sourceJSON,
790
+ meta: targetJSON.meta,
791
+ };
792
+ return jsonToNVM(json, targetJSON.controller.protocolVersion);
793
+ }
794
+ }
795
+ exports.migrateNVM = migrateNVM;
796
+ //# sourceMappingURL=convert.js.map