0.4.9 • Published 2 months ago

usp-js v0.4.9

Weekly downloads
103
License
BSD-3-Clause
Repository
-
Last release
2 months ago

usp-js

Helper library for easy usp communication using mqtt over tcp or ws.

Installation

npm install usp-js

Quick Example

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;

const run = async () => {
  // Connect
  const usp = await connect({
    host: "my.ip.here",
    username: "username",
    password: "password",
    port: 9001,
    protocol: "ws",
  });

  // Get property
  await usp.get("Device.WiFi.").then(console.log);

  // Disconnect
  await usp.disconnect();
};

run();

API

connect

The most commonly used connection options (usp-js extends the connection options provided by mqtt.js):

  • 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

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
const result = await usp.get("Device.Time.");
  • get raw message

Returns full USP message as sent by the device, mostly useful for debugging purposes.

const result = await usp.get("Device.Time.", { raw: true });
  • get with max depth (requires usp version 1.2)

Max depth determines how granular the result is for large objects, defaults to 2.

const result = await usp.get("Device.Time.", { max_depth: 2 });
  • get nested

Results include "__query__" variable which holds the full path

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",
    },
  ],
};
  • get with error

USP errors will be thrown from the get call, while connection errors can be caught using the onError handler when connecting (example above)

await usp.get("Fake.Path").then(console.log).catch(console.error);
  • get multiple paths

Can retrieve multiple paths at the same time, result will always be in an array.

await usp.get(["Device.WiFi.Radio.1.Alias", "Device.WiFi.Radio.2.Alias"]);
  • get using pattern

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).

await usp.get('Device.Ethernet.Interface.[Alias=="WAN"].CurrentBitRate');
  • resolve references in get

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.

await usp.get("Device.NAT.InterfaceSetting.1.");
const normalResult = {
  Status: "Disabled",
  Interface: "Device.IP.Interface.1",
  Enable: "0",
  Alias: "cpe-1",
};

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",
};

set

  • set object - does not need to have all attributes, but some may be required (check USP Reference)
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",
    },
  },
];
  • set object with allowPartial and required attributes
await usp.set("Device.WiFi.Radio.1.", {
  Name: { required: true, value: "radio-1" },
  allowPartial: true,
});
  • set property
await usp.set("Device.WiFi.Radio.1.Name", "radio-1");
  • set multiple
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.

const [ping, cleanPing] = await usp.operate("Device.IP.Diagnostics.IPPing()");
const results = await ping({ Host: "iopsys.eu" });
await cleanPing(); // clears ping subscription (optional)
  • Sync command
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.

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
await usp.add("Device.NAT.PortMapping."); // => "Device.NAT.PortMapping.3."
  • add with multiple responses
await usp.add("Device.IP.Interface.*.IPv4Address."); // => ['Device.IP.Interface.1.IPv4Address.2.', ... ]
  • add with arguments - adds a new object with provided values
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
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

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
await usp.supportedDM("Device.WiFi.");
  • retrieve entire domain model
await usp.supportedDM("Device.", {
  firstLevelOnly: false,
  returnCommands: true,
  returnEvents: true,
  returnParams: true,
});

supportedProto

Retrieves supported protocols.

await usp.supportedProto("Device.");

instances

await usp.instances("Device.WiFi.");

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.

const clearSub = await usp.subscribe(
  { id: "1234", notif: "ObjectCreation", reference: "Device.NAT.PortMapping." },
  (msg, fullMsg) => console.log({ msg, fullMsg })
);

await clearSub();

on

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.

const clear = usp.on(/NOTIFY.*/, (msg, rawMsg) => console.log({ msg, rawMsg }));

options

Options allows extending commands with additional functionality:

  • 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
await usp
  .options({
    timeout: 1000,
    preCall: (name, args) => console.log(name, args),
    postCall: (name, args, result) => console.log(name, args, result),
    get: { retainPath: true },
  })
  .get("Device.DeviceInfo.SerialNumber");

To apply the options globally add the call to the connect function.

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.

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.

usp.setUSPVersion("1.2");

disconnect

Close connection.

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:

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:
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:
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.

0.4.9

2 months ago

0.4.8

6 months ago

0.4.5

9 months ago

0.4.4

9 months ago

0.4.7

7 months ago

0.4.6

7 months ago

0.4.3

9 months ago

0.4.2

11 months ago

0.3.8

1 year ago

0.3.9

1 year ago

0.3.10

1 year ago

0.4.1

12 months ago

0.4.0

1 year ago

0.3.6

1 year ago

0.3.5

1 year ago

0.3.1

2 years ago

0.3.4

1 year ago

0.3.3

1 year ago

0.2.26

2 years ago

0.3.0

2 years ago

0.2.25

2 years ago

0.2.24

2 years ago

0.2.22

2 years ago

0.2.21

2 years ago

0.2.20

2 years ago

0.2.19

2 years ago

0.2.18

2 years ago

0.2.17

2 years ago

0.2.14

2 years ago

0.2.13

2 years ago

0.2.12

2 years ago

0.2.11

2 years ago

0.2.10

2 years ago

0.2.7

2 years ago

0.2.6

2 years ago

0.2.9

2 years ago

0.2.8

2 years ago

0.2.5

3 years ago

0.2.4

3 years ago

0.2.3

3 years ago

0.2.1

3 years ago

0.2.2

3 years ago

0.1.20

3 years ago

0.1.21

3 years ago

0.1.22

3 years ago

0.1.17

3 years ago

0.1.18

3 years ago

0.1.19

3 years ago

0.1.15

3 years ago

0.1.14

3 years ago

0.1.12

3 years ago

0.1.13

3 years ago

0.1.10

3 years ago

0.1.11

3 years ago

0.1.9

3 years ago

0.1.8

3 years ago

0.1.7

3 years ago

0.1.6

3 years ago

0.1.5

3 years ago

0.1.4

3 years ago

0.1.3

3 years ago

0.1.2

3 years ago

0.1.0

3 years ago

0.1.1

3 years ago

0.0.12

3 years ago

0.0.10

3 years ago

0.0.11

3 years ago

0.0.9

3 years ago

0.0.8

3 years ago

0.0.7

3 years ago

0.0.5

3 years ago

0.0.6

3 years ago

0.0.3

3 years ago

0.0.4

3 years ago

0.0.2

3 years ago

0.0.1

3 years ago