Newer
Older
Helper library for easy usp communication using mqtt over tcp or ws.
- [API documentation for usp-js](https://iopsys.se/usp-js/index.html)
- [BBF's USP reference documentation](https://usp-data-models.broadband-forum.org/tr-181-2-14-0-usp.html)
Marin Karamihalev
committed
# Quick Example
Marin Karamihalev
committed
To connect provide necessary info to the default export.
Note: for browser usage make sure to import the library from usp-js/web.
const connect = require("usp-js").default;
host: "my.ip.here",
username: "username",
password: "password",
Marin Karamihalev
committed
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
### connect
The most commonly used connection options (usp-js extends the connection options provided by [mqtt.js](https://github.com/mqttjs/MQTT.js#mqttconnecturl-options)):
- host (string): host address,
- protocol (string): protocol to use (i.e. ws, wss, mqtt, etc),
- port (number): port number,
- username (string): log in username,
- password (string): log in password,
- useLatestUSPVersion (boolean): if set to true will automatically retrieve and use latest version of usp protocol available on the device (can instead be set manually by using the "version" option)
The connect function also has several events that can be used to keep track of the connection state:
- onError ((err: string) => void): Handler for client errors, will not trigger on usp error (for instance trying to get a non-existent path),
- onOffline (() => void): triggers when connection goes offline,
- onReconnect (() => void): triggers when connection reconnects (can use options like "reconnectsBeforeClosing" to control reconnection attempts),
- onClose (() => void): triggers when connection is closed
Example
```typescript
const usp = await connect(
{
host: "host.ip",
protocol: "ws",
port: 9001,
username: "username",
password: "password",
useLatestUSPVersion: true,
},
{
onError: (err) => console.error("connection error:", err),
onOffline: () => console.log("connection offline"),
onReconnect: () => console.log("connection reconnecting"),
onClose: () => console.log("connection closed"),
}
);
```
### get
- get single item
Marin Karamihalev
committed
const result = await usp.get("Device.Time.");
Marin Karamihalev
committed
- get raw message
Marin Karamihalev
committed
Returns full USP message as sent by the device, mostly useful for debugging purposes.
Marin Karamihalev
committed
```javascript
const result = await usp.get("Device.Time.", { raw: true });
```
Marin Karamihalev
committed
- get with max depth (requires usp version 1.2)
Marin Karamihalev
committed
Max depth determines how granular the result is for large objects, defaults to 2.
Marin Karamihalev
committed
```javascript
const result = await usp.get("Device.Time.", { max_depth: 2 });
```
Marin Karamihalev
committed
- get nested
Marin Karamihalev
committed
Results include "\_\_query\_\_" variable which holds the full path
Marin Karamihalev
committed
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
```javascript
await usp.get("Device.NAT.PortMapping.");
const standardGet = [
{
Status: "Disabled",
RemoteHost: "",
Protocol: "TCP",
LeaseDuration: "0",
InternalPort: "0",
InternalClient: "",
Interface: "",
ExternalPortEndRange: "0",
ExternalPort: "0",
Enable: "0",
Description: "cat-1",
AllInterfaces: "0",
Alias: "cpe-1",
},
];
await usp.getNested("Device.NAT.PortMapping.");
const nestedGet = {
__query__: "Device.NAT.PortMapping.",
result: [
{
__query__: "Device.NAT.PortMapping.1.",
Alias: "cpe-1",
AllInterfaces: "0",
Description: "cat-1",
Enable: "0",
ExternalPort: "0",
ExternalPortEndRange: "0",
Interface: "",
InternalClient: "",
InternalPort: "0",
LeaseDuration: "0",
Protocol: "TCP",
RemoteHost: "",
Status: "Disabled",
},
],
};
```
Marin Karamihalev
committed
- get with error
Marin Karamihalev
committed
USP errors will be thrown from the get call, while connection errors can be caught using the onError handler when connecting (example above)
Marin Karamihalev
committed
```javascript
await usp.get("Fake.Path").then(console.log).catch(console.error);
```
Marin Karamihalev
committed
- get multiple paths
Marin Karamihalev
committed
Can retrieve multiple paths at the same time, result will always be in an array.
Marin Karamihalev
committed
```javascript
await usp.get(["Device.WiFi.Radio.1.Alias", "Device.WiFi.Radio.2.Alias"]);
```
Marin Karamihalev
committed
- get using pattern
Marin Karamihalev
committed
Can use search patterns to search for specific object, results are always in an array (patterns are based on USP implementation, they are not handled by usp-js).
Marin Karamihalev
committed
```javascript
await usp.get('Device.Ethernet.Interface.[Alias=="WAN"].CurrentBitRate');
```
Marin Karamihalev
committed
- resolve references in get
Marin Karamihalev
committed
Many objects in the model hold references to other objects. To resolve those quickly, use the resolve function. Same functionality can be achieved by manually retrieving references and using get on them, resolve is just for convenience. Resolve also provides an optional level argument (defaults to 1) which allows retrieving multiple levels of references. Can be useful for complicated objects but it is best to avoid setting it to values above 3 as there can be circular references.
Marin Karamihalev
committed
```javascript
await usp.get("Device.NAT.InterfaceSetting.1.");
const normalResult = {
Status: "Disabled",
Interface: "Device.IP.Interface.1",
Enable: "0",
Alias: "cpe-1",
};
Marin Karamihalev
committed
await usp.get("Device.NAT.InterfaceSetting.1.").then(usp.resolve);
// some values skipped for brevity
const resolvedResult = {
Status: "Disabled",
Interface: {
ULAEnable: "1",
Type: "Normal",
TWAMPReflectorNumberOfEntries: "0",
Status: "Down",
Enable: "1",
Alias: "cpe-1",
},
Enable: "0",
Alias: "cpe-1",
};
```
Marin Karamihalev
committed
### set
Marin Karamihalev
committed
- set object - does not need to have all attributes, but some may be required (check USP Reference)
Marin Karamihalev
committed
```javascript
await usp.set("Device.NAT.PortMapping.1.", { Description: "a very nice port" });
// returns array of affected paths
const result = [
{
affectedPath: "Device.NAT.PortMapping.1.",
updatedParams: {
Description: "a very nice port",
},
},
];
```
Marin Karamihalev
committed
- set object with allowPartial and required attributes
Marin Karamihalev
committed
```javascript
await usp.set("Device.WiFi.Radio.1.", {
Name: { required: true, value: "radio-1" },
allowPartial: true,
});
```
Marin Karamihalev
committed
- set property
Marin Karamihalev
committed
```javascript
await usp.set("Device.WiFi.Radio.1.Name", "radio-1");
```
Marin Karamihalev
committed
- set multiple
Marin Karamihalev
committed
await usp.set(
["Device.WiFi.Radio.1.Name", "Device.WiFi.Radio.2."],
["radio-1", { Name: "radio-2" }]
);
```
### operate
- Async command
Operate assumes the command is async by default.
```javascript
const [ping, cleanPing] = await usp.operate("Device.IP.Diagnostics.IPPing()");
const results = await ping({ Host: "iopsys.eu" });
await cleanPing(); // clears ping subscription (optional)
Marin Karamihalev
committed
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
- Sync command
```javascript
const [reset] = await usp.operate("Device.FactoryReset()", {
isAsync: false,
});
const results = await reset();
```
- Command with subscription
The returned operate command provides a way to subscribe to call results.
```javascript
const [ping, cleanPing] = await usp.operate("Device.IP.Diagnostics.IPPing()");
ping.subscribe((msg) => console.log(msg));
await ping({ Host: "iopsys.eu" });
await ping({ Host: "google.se" });
await cleanPing();
```
### add
- add with no arguments - adds a new default object
```javascript
await usp.add("Device.NAT.PortMapping."); // => "Device.NAT.PortMapping.3."
```
- add with multiple responses
```javascript
await usp.add("Device.IP.Interface.*.IPv4Address."); // => ['Device.IP.Interface.1.IPv4Address.2.', ... ]
```
- add with arguments - adds a new object with provided values
```javascript
await usp.add("Device.NAT.PortMapping.", {
Description: "webserver1-set",
ExternalPort: "80",
Protocol: "TCP",
Interface: "Device.IP.Interface.1",
Enable: "true",
InternalClient: "192.168.1.125",
InternalPort: "5000",
}); // => "Device.NAT.PortMapping.4."
```
- add with with allowPartial and required attributes
```javascript
await usp.add("Device.NAT.PortMapping.", {
allowPartial: true,
Description: {
required: true,
value: "webserver1-set",
}
ExternalPort: "80",
Protocol: "TCP",
Interface: "Device.IP.Interface.1",
Enable: "true",
InternalClient: "192.168.1.125",
InternalPort: "5000",
}); // => "Device.NAT.PortMapping.4."
```
### del
```javascript
await usp.del("Device.NAT.PortMapping.4.");
// returns list of affected paths
const result = ["Device.NAT.PortMapping.4."];
```
### supportedDM
Retrieves supported domain model.
- standard call
Marin Karamihalev
committed
Marin Karamihalev
committed
await usp.supportedDM("Device.WiFi.");
Marin Karamihalev
committed
Marin Karamihalev
committed
- retrieve entire domain model
Marin Karamihalev
committed
Marin Karamihalev
committed
await usp.supportedDM("Device.", {
firstLevelOnly: false,
returnCommands: true,
returnEvents: true,
returnParams: true,
});
Marin Karamihalev
committed
### supportedProto
Retrieves supported protocols.
Marin Karamihalev
committed
await usp.supportedProto("Device.");
Marin Karamihalev
committed
### instances
Marin Karamihalev
committed
await usp.instances("Device.WiFi.");
Marin Karamihalev
committed
### subscribe
The subscription callback provides both the formatted message and the raw USP message. The returned function provides an easy way to clear the subscription.
Marin Karamihalev
committed
```javascript
Marin Karamihalev
committed
const clearSub = await usp.subscribe(
Marin Karamihalev
committed
{ id: "1234", notif: "ObjectCreation", reference: "Device.NAT.PortMapping." },
Marin Karamihalev
committed
(msg, fullMsg) => console.log({ msg, fullMsg })
Marin Karamihalev
committed
);
Marin Karamihalev
committed
await clearSub();
Marin Karamihalev
committed
```
Marin Karamihalev
committed
### on
Marin Karamihalev
committed
The "on" function is internal to usp-js. It provides a way to subscribe to incoming messages without directly interacting with the device (it will not add a subscription to the local agent). It is mostly used for debugging purposes.
The first argument is the id of the message to subscribe to. It can be a string or a regexp. The general message id has the shape: COMMAND@random_string (i.e. NOTIFY@12345).
The second argument is a callback that optionally provides access to the raw USP message.
Marin Karamihalev
committed
const clear = usp.on(/NOTIFY.*/, (msg, rawMsg) => console.log({ msg, rawMsg }));
Marin Karamihalev
committed
### options
Marin Karamihalev
committed
Marin Karamihalev
committed
Options allows extending commands with additional functionality:
Marin Karamihalev
committed
Marin Karamihalev
committed
- timeout: timeout after given ms (throws error)
- preCall: called before each command, provides command name and arguments
- postCall: called after each command, provides command name, arguments and result/error
- get.retainPath: sets "retainPath" option to true for all get calls, equivalent to replacing getNeset everywhere
Marin Karamihalev
committed
```javascript
await usp
.options({
timeout: 1000,
preCall: (name, args) => console.log(name, args),
postCall: (name, args, result) => console.log(name, args, result),
Marin Karamihalev
committed
get: { retainPath: true },
})
.get("Device.DeviceInfo.SerialNumber");
Marin Karamihalev
committed
```
Marin Karamihalev
committed
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
To apply the options globally add the call to the connect function.
```javascript
const usp = (await connect(connectionOptions)).options({
timeout: 1000,
preCall: (name, args) => console.log(name, args),
postCall: (name, args, result) => console.log(name, args, result),
get: { retainPath: true },
});
```
### getUSPVersion
Retrieves which usp version is currently being used by usp-js.
```javascript
usp.getUSPVersion(); // => "1.2"
```
### setUSPVersion
Change which usp version is currently being used. This will only change which version usp-js is using, not the usp agent (mostly for debugging purposes). Generally, it is preferable to use the "useLatestUspVersion" option when connecting instead of setting the version manually.
```javascript
usp.setUSPVersion("1.2");
```
### disconnect
Close connection.
```javascript
await usp.disconnect();
```
# Making a new connect function
To allow more customization, new connect functions can be made to fit specific environments. By default, usp-js provides a version of connect for mqtt, ws and quickjs. To create a new connect, use the buildConnect function:
```typescript
const myConnect = buildConnect({
connectClient,
decodeID,
loadProtobuf,
});
const usp = await myConnect(connectionOptions);
```
- connectClient
The connectClient function is an async function that accepts connection options and returns a connection object. Below is an example of how the websocket client can be adapted:
```typescript
const connectClient: ConnectClientFn = (opts: any) =>
new Promise((resolve, reject) => {
const client = new WebSocketClient();
client.on("connectFailed", (error) => {
reject(error);
});
client.on("connect", (ws) => {
resolve({
on: (key, responseFn) => ws.on(key, (data) => responseFn(key, data)),
subscribe: (to) => null,
unsubscribe: (from) => null,
publish: (endpoint, msg) => ws.send(msg),
end: ws.close,
});
});
client.connect(
`${opts.protocol}://${opts.host}:${opts.port}/usp/endpoint`,
"v1.usp"
);
});
```
- decodeID
The decodeID function is used when a message arrives to retrieve the id from the raw data. The default function simply converts the data to a string:
```typescript
const decodeID = (data: any) => String(data);
```
- loadProtobuf
The loadProtobuf function is used to load the protobuf files used to convert incoming messages into javascript legible data. It is provided as an option to buildConnect, since some environment can be restrictive about how files are loaded. It is possible to import the loadProtobuf function from configurations/mqtt if no alterations are required.
# Quickjs
usp-js provides a quickjs compatible version. To use it, simply import from usp-js/qjs.
### Quickjs development
Run the following command after making any changes to the base code:
```
yarn run qjs
```
This script needs to be run because compilation for quickjs requires several direct changes to the code.