@blueyerobotics/blueye-ts 1.5.0 → 2.0.0

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/README.md CHANGED
@@ -15,13 +15,19 @@ import { BlueyeClient } from "@blueyerobotics/blueye-ts";
15
15
 
16
16
  const client = new BlueyeClient();
17
17
 
18
- const rep = await client.sendRequest("GetBatteryReq");
19
- const tel = await client.getTelemetry("BatteryTel");
18
+ // request battery information
19
+ const batteryRep = await client.sendRequest("GetBatteryReq");
20
+ console.log("batteryRep:", batteryRep);
20
21
 
21
- console.log("Rep:", rep);
22
- console.log("Tel:", tel);
22
+ // get latest battery telemetry
23
+ const batteryTel = await client.getTelemetry("BatteryTel");
24
+ console.log("batteryTel:", batteryTel);
23
25
 
24
- client.sub.on("BatteryTel", data => {
25
- console.log("Received BatteryTel:", data);
26
+ // send a control message to turn on the lights
27
+ await client.sendControl("LightsCtrl", { lights: { value: 1 } });
28
+
29
+ // subscribe to battery telemetry updates
30
+ client.on("BatteryTel", data => {
31
+ console.log("received BatteryTel:", data);
26
32
  });
27
33
  ```
package/dist/example.js CHANGED
@@ -3,12 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const index_1 = require("./index");
4
4
  const main = async () => {
5
5
  const client = new index_1.BlueyeClient();
6
- const rep = await client.sendRequest("GetBatteryReq");
7
- const tel = await client.getTelemetry("BatteryTel");
8
- console.log("Rep:", rep);
9
- console.log("Tel:", tel);
10
- client.sub.on("BatteryTel", data => {
11
- console.log("Received BatteryTel:", data);
6
+ // request battery information
7
+ const batteryRep = await client.sendRequest("GetBatteryReq");
8
+ console.log("batteryRep:", batteryRep);
9
+ // get latest battery telemetry
10
+ const batteryTel = await client.getTelemetry("BatteryTel");
11
+ console.log("batteryTel:", batteryTel);
12
+ // send a control message to turn on the lights
13
+ await client.sendControl("LightsCtrl", { lights: { value: 1 } });
14
+ // subscribe to battery telemetry updates
15
+ client.on("BatteryTel", data => {
16
+ console.log("received BatteryTel:", data);
12
17
  });
13
18
  };
14
19
  main();
@@ -9,23 +9,22 @@ export type Rep = keyof Pick<Protocol, Extract<ProtocolKey, `${string}Rep`>>;
9
9
  export type Tel = keyof Pick<Protocol, Extract<ProtocolKey, `${string}Tel`>>;
10
10
  export type Ctrl = keyof Pick<Protocol, Extract<ProtocolKey, `${string}Ctrl`>>;
11
11
  export type ReqToRep<T extends Req> = T extends `${infer Prefix}Req` ? `${Prefix}Rep` extends ProtocolKey ? Protocol[`${Prefix}Rep`] : never : never;
12
- export type MsgHandler<T extends Req> = Protocol[T];
13
- export type CreateArgs<T extends Req> = Parameters<MsgHandler<T>["create"]>[0];
12
+ export type MsgHandler<T extends Req | Ctrl> = Protocol[T];
13
+ export type CreateArgs<T extends Req | Ctrl> = Parameters<MsgHandler<T>["create"]>[0];
14
14
  export type DecodedOutput<T extends Req> = ReturnType<ReqToRep<T>["decode"]>;
15
15
  export type DecodedTelOutput<T extends Tel> = ReturnType<Protocol[T]["decode"]>;
16
16
  export type Events = {
17
17
  [K in Tel]: [DecodedTelOutput<K>];
18
18
  };
19
19
  export declare const isInProtocol: (key: string) => key is keyof typeof blueye.protocol;
20
- export declare class BlueyeClient {
20
+ export declare class BlueyeClient extends Emitter<Events> {
21
21
  timeout: number;
22
- private wsPubSub;
23
- private wsReqRep;
24
- private isReqRepConnected;
22
+ private sub;
23
+ private rpc;
24
+ private pub;
25
25
  private logger;
26
- private pendingRequests;
27
- sub: Emitter<Events>;
28
26
  constructor(timeout?: number, logLevel?: LogLevel);
29
27
  sendRequest<T extends Req>(req: T, opts?: CreateArgs<T>): Promise<DecodedOutput<T> | null>;
30
28
  getTelemetry<T extends Tel>(type: T): Promise<DecodedTelOutput<T>>;
29
+ sendControl<T extends Ctrl>(ctrl: T, opts?: CreateArgs<T>): Promise<void>;
31
30
  }
@@ -4,93 +4,94 @@ exports.BlueyeClient = exports.isInProtocol = void 0;
4
4
  const protocol_definitions_1 = require("@blueyerobotics/protocol-definitions");
5
5
  const buffer_1 = require("buffer");
6
6
  const consola_1 = require("consola");
7
+ const jszmq_1 = require("jszmq");
7
8
  const strict_event_emitter_1 = require("strict-event-emitter");
8
- const uuid_1 = require("uuid");
9
9
  const schema_1 = require("./schema");
10
- const WS_PUBSUB_URL = "ws://192.168.1.101:8765";
11
- const WS_REQREP_URL = "ws://192.168.1.101:8766";
10
+ const SUB_URL = "ws://192.168.1.101:9985";
11
+ const RPC_URL = "ws://192.168.1.101:9986";
12
+ const PUB_URL = "ws://192.168.1.101:9987";
12
13
  const isInProtocol = (key) => {
13
14
  return key in protocol_definitions_1.blueye.protocol;
14
15
  };
15
16
  exports.isInProtocol = isInProtocol;
16
- class BlueyeClient {
17
+ class BlueyeClient extends strict_event_emitter_1.Emitter {
17
18
  timeout;
18
- wsPubSub;
19
- wsReqRep;
20
- isReqRepConnected = false;
19
+ sub;
20
+ rpc;
21
+ pub;
21
22
  logger;
22
- pendingRequests = new Map();
23
- sub = new strict_event_emitter_1.Emitter();
24
23
  constructor(timeout = 2000, logLevel = consola_1.LogLevels.info) {
24
+ super();
25
25
  this.timeout = timeout;
26
26
  this.logger = (0, consola_1.createConsola)({ level: logLevel, formatOptions: { colors: true, compact: false } });
27
- this.wsPubSub = new WebSocket(WS_PUBSUB_URL);
28
- this.wsReqRep = new WebSocket(WS_REQREP_URL);
29
- this.wsPubSub.addEventListener("open", () => {
30
- this.logger.info("[WS] PubSub connected");
31
- });
32
- this.wsReqRep.addEventListener("open", () => {
33
- this.isReqRepConnected = true;
34
- this.logger.info("[WS] ReqRep connected");
35
- });
36
- this.wsPubSub.addEventListener("message", event => {
37
- const { key, data } = schema_1.responseSchema.parse(JSON.parse(event.data));
27
+ this.sub = new jszmq_1.Sub();
28
+ this.rpc = new jszmq_1.Req();
29
+ this.pub = new jszmq_1.Pub();
30
+ this.sub.subscribe("");
31
+ this.sub.connect(SUB_URL);
32
+ this.rpc.connect(RPC_URL);
33
+ this.pub.connect(PUB_URL);
34
+ // @ts-ignore
35
+ this.sub.on("message", (topic, msg) => {
36
+ const { key, data } = schema_1.responseSchema.parse({ key: topic, data: msg });
37
+ if (!(0, exports.isInProtocol)(key) || !key.endsWith("Tel")) {
38
+ this.logger.warn("[sub] unknown protocol:", key);
39
+ return;
40
+ }
38
41
  const protocol = protocol_definitions_1.blueye.protocol[key];
39
42
  const message = protocol.decode(data);
40
- this.logger.verbose("[WS] PubSub message:", key, message);
41
- this.sub.emit(key, message);
42
- });
43
- this.wsReqRep.addEventListener("message", event => {
44
- this.logger.debug("Response:", event.data);
45
- const { id, key, data } = schema_1.responseSchema.parse(JSON.parse(event.data));
46
- if (!id)
47
- throw new Error("Response id is missing");
48
- this.pendingRequests.get(id)?.({ key, data });
43
+ this.logger.verbose("[sub] message:", key, message);
44
+ this.emit(key, message);
49
45
  });
50
46
  }
51
47
  async sendRequest(req, opts = {}) {
48
+ if (!(0, exports.isInProtocol)(req) || !req.endsWith("Req")) {
49
+ throw new Error(`[rpc] unknown protocol: ${req}`);
50
+ }
52
51
  const protocol = protocol_definitions_1.blueye.protocol[req];
53
52
  const message = protocol.create(opts);
54
53
  const encoded = protocol.encode(message).finish();
55
- while (!this.isReqRepConnected) {
56
- this.logger.debug("Waiting...");
57
- await new Promise(res => setTimeout(res, 50));
58
- }
59
- const id = (0, uuid_1.v4)();
60
54
  const { key, data } = await Promise.race([
61
- new Promise((_, reject) => setTimeout(() => reject(new Error("Request timed out")), this.timeout)),
55
+ new Promise((_, reject) => setTimeout(() => reject(new Error("[rpc] request timed out")), this.timeout)),
62
56
  new Promise(resolve => {
63
- const request = JSON.stringify({
64
- id,
65
- key: `blueye.protocol.${req}`,
66
- data: buffer_1.Buffer.from(encoded).toString("base64")
57
+ // @ts-ignore
58
+ this.rpc.once("message", (topic, msg) => {
59
+ resolve({ key: topic.toString().split(".").at(-1), data: msg });
67
60
  });
68
- this.pendingRequests.set(id, resolve);
69
- this.wsReqRep.send(request);
61
+ this.rpc.send([buffer_1.Buffer.from(`blueye.protocol.${req}`), buffer_1.Buffer.from(encoded)]);
70
62
  })
71
63
  ]);
72
- this.pendingRequests.delete(id);
73
64
  if (key === "Empty") {
74
65
  return null;
75
66
  }
67
+ if (!(0, exports.isInProtocol)(key) || !key.endsWith("Rep")) {
68
+ throw new Error(`[rpc] unknown response protocol: ${key}`);
69
+ }
76
70
  const rep = protocol_definitions_1.blueye.protocol[key];
77
71
  const result = rep.decode(data);
78
- this.logger.debug("Decoded:", result);
72
+ this.logger.debug("[rpc] decoded:", result);
79
73
  return result;
80
74
  }
81
75
  async getTelemetry(type) {
82
76
  const response = await this.sendRequest("GetTelemetryReq", { messageType: type });
83
77
  const { payload } = schema_1.telemetrySchema.parse(response);
84
78
  const { typeUrl, value } = payload;
85
- this.logger.debug(typeUrl);
86
- if ((0, exports.isInProtocol)(typeUrl)) {
87
- const result = protocol_definitions_1.blueye.protocol[typeUrl].decode(value);
88
- this.logger.debug("Result:", result);
89
- return result;
79
+ if (!(0, exports.isInProtocol)(typeUrl) || !typeUrl.endsWith("Tel")) {
80
+ throw new Error(`[rpc] unknown telemetry typeUrl: ${typeUrl}`);
90
81
  }
91
- else {
92
- throw new Error("Unknown typeUrl");
82
+ const result = protocol_definitions_1.blueye.protocol[typeUrl].decode(value);
83
+ this.logger.debug("[rpc] result:", result);
84
+ return result;
85
+ }
86
+ async sendControl(ctrl, opts = {}) {
87
+ if (!(0, exports.isInProtocol)(ctrl) || !ctrl.endsWith("Ctrl")) {
88
+ throw new Error(`[pub] unknown protocol: ${ctrl}`);
93
89
  }
90
+ const protocol = protocol_definitions_1.blueye.protocol[ctrl];
91
+ const message = protocol.create(opts);
92
+ const encoded = protocol.encode(message).finish();
93
+ this.logger.debug("[pub] sending control:", ctrl, message);
94
+ this.pub.send([buffer_1.Buffer.from(`blueye.protocol.${ctrl}`), buffer_1.Buffer.from(encoded)]);
94
95
  }
95
96
  }
96
97
  exports.BlueyeClient = BlueyeClient;
@@ -1,17 +1,14 @@
1
1
  import { Buffer } from "buffer";
2
2
  import z from "zod";
3
3
  export declare const responseSchema: z.ZodObject<{
4
- id: z.ZodOptional<z.ZodString>;
5
- key: z.ZodEffects<z.ZodString, string, string>;
6
- data: z.ZodEffects<z.ZodString, Buffer, string>;
4
+ key: z.ZodEffects<z.ZodType<Buffer, z.ZodTypeDef, Buffer>, string, Buffer>;
5
+ data: z.ZodType<Buffer, z.ZodTypeDef, Buffer>;
7
6
  }, "strip", z.ZodTypeAny, {
8
7
  key: string;
9
8
  data: Buffer;
10
- id?: string | undefined;
11
9
  }, {
12
- key: string;
13
- data: string;
14
- id?: string | undefined;
10
+ key: Buffer;
11
+ data: Buffer;
15
12
  }>;
16
13
  export declare const telemetrySchema: z.ZodObject<{
17
14
  payload: z.ZodObject<{
@@ -7,12 +7,8 @@ exports.telemetrySchema = exports.responseSchema = void 0;
7
7
  const buffer_1 = require("buffer");
8
8
  const zod_1 = __importDefault(require("zod"));
9
9
  exports.responseSchema = zod_1.default.object({
10
- id: zod_1.default.string().uuid().optional(),
11
- key: zod_1.default.string().transform(val => val.split(".").at(-1)),
12
- data: zod_1.default
13
- .string()
14
- .base64()
15
- .transform(val => buffer_1.Buffer.from(val, "base64"))
10
+ key: zod_1.default.instanceof(buffer_1.Buffer).transform(val => val.toString().split(".").at(-1)),
11
+ data: zod_1.default.instanceof(buffer_1.Buffer)
16
12
  });
17
13
  exports.telemetrySchema = zod_1.default.object({
18
14
  payload: zod_1.default.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueyerobotics/blueye-ts",
3
- "version": "1.5.0",
3
+ "version": "2.0.0",
4
4
  "description": "A TypeScript client for interacting with Blueye underwater drones.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,8 +22,8 @@
22
22
  "@blueyerobotics/protocol-definitions": "3.2.0-0f78a956",
23
23
  "buffer": "^6.0.3",
24
24
  "consola": "^3.4.2",
25
+ "jszmq": "^0.1.2",
25
26
  "strict-event-emitter": "^0.5.1",
26
- "uuid": "^11.1.0",
27
27
  "zod": "^3.25.67"
28
28
  },
29
29
  "devDependencies": {