@ya-modbus/transport 0.11.2 → 0.12.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/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [0.12.0](https://github.com/groupsky/ya-modbus/compare/@ya-modbus/transport@0.11.2...@ya-modbus/transport@0.12.0) (2026-10-12)
7
+
8
+ ### Features
9
+
10
+ - **transport:** list serial ports with their USB ids and locations ([#596](https://github.com/groupsky/ya-modbus/issues/596)) ([0ad4a5a](https://github.com/groupsky/ya-modbus/commit/0ad4a5ae8577154f2b198eec3c019e24ca4b5ca3)), closes [#534](https://github.com/groupsky/ya-modbus/issues/534)
11
+
6
12
  ## [0.11.2](https://github.com/groupsky/ya-modbus/compare/@ya-modbus/transport@0.11.1...@ya-modbus/transport@0.11.2) (2026-10-12)
7
13
 
8
14
  ### Bug Fixes
package/README.md CHANGED
@@ -207,6 +207,61 @@ When the first `open()` fails, you can pass its error instead of the client:
207
207
  error, and opens the port later in the same way. `connection.error` is the error while the
208
208
  connection is lost, and `undefined` while it works.
209
209
 
210
+ ## Finding serial ports
211
+
212
+ `listSerialPorts()` lists the serial ports with their USB identity and their location (the
213
+ physical USB port):
214
+
215
+ ```typescript
216
+ import { listSerialPorts, matchSerialPorts } from '@ya-modbus/transport'
217
+
218
+ const ports = await listSerialPorts()
219
+ // [
220
+ // {
221
+ // path: '/dev/ttyUSB0',
222
+ // vendorId: '1a86',
223
+ // productId: '7523',
224
+ // locations: [
225
+ // 'pci-0000:00:1a.0-usb-0:1.3:1.0-port0',
226
+ // 'pci-0000:00:1a.0-usbv2-0:1.3:1.0-port0',
227
+ // ],
228
+ // },
229
+ // { path: '/dev/ttyS0', locations: [] },
230
+ // ]
231
+ ```
232
+
233
+ - `vendorId` and `productId` are 4 lowercase hex digits. They are missing for ports that are not
234
+ USB adapters.
235
+ - `serialNumber` and `manufacturer` are there only when the adapter reports them, with the value
236
+ as the adapter reports it. Many CH340 adapters have no serial number.
237
+ - `locations` are the names of the links in `/dev/serial/by-path` to the port. Newer systemd
238
+ versions make two links for the same USB port (`...-usb-...` and `...-usbv2-...`). The list is
239
+ empty when the port has no link, for example on other systems than Linux.
240
+
241
+ On Linux the ports are read from `/sys/class/tty`. This starts no other program, so it also works
242
+ in a hardened service. On other systems the list comes from the `serialport` package (through
243
+ `modbus-serial`).
244
+
245
+ In a container, `/sys` shows all serial ports of the host, also the ones that are not passed into
246
+ the container. `/dev/serial/by-path` exists only when it is mounted into the container. Without
247
+ it, `locations` are empty.
248
+
249
+ `matchSerialPorts(ports, match)` returns the ports that match all given values. It does no I/O.
250
+ `vendorId` and `productId` are required. Leave out `serialNumber` or `location` to match any.
251
+ `location` is a name from `locations`, or the full `/dev/serial/by-path/...` path. Several
252
+ identical adapters without a serial number all match, unless `location` selects one of them:
253
+
254
+ ```typescript
255
+ const matches = matchSerialPorts(ports, {
256
+ vendorId: '1a86',
257
+ productId: '7523',
258
+ location: 'pci-0000:00:1a.0-usb-0:1.3:1.0-port0',
259
+ })
260
+ if (matches.length === 1) {
261
+ // matches[0].path is the port to open
262
+ }
263
+ ```
264
+
210
265
  ## License
211
266
 
212
267
  GPL-3.0-or-later
@@ -14,4 +14,5 @@ export { isConnectionLostError, ModbusConnection, type OpenClient } from './conn
14
14
  export { TransportManager, type TransportManagerOptions, type TransportStats } from './manager.js';
15
15
  export { MutexTransport } from './mutex-transport.js';
16
16
  export { SlaveTransport } from './slave-transport.js';
17
+ export { listSerialPorts, matchSerialPorts, type SerialPortInfo, type SerialPortMatch, } from './serial-ports.js';
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACrF,OAAO,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC1F,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAClG,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACrF,OAAO,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC1F,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAClG,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAA"}
@@ -6,7 +6,7 @@
6
6
  * and transport pooling with mutex-based RTU bus serialization
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.SlaveTransport = exports.MutexTransport = exports.TransportManager = exports.ModbusConnection = exports.isConnectionLostError = exports.normalizeModbusError = exports.RETRY_DELAY_MS = exports.MAX_RETRIES = exports.withRetry = exports.createModbusTransport = exports.createTCPTransport = exports.createRTUTransport = exports.createTransport = void 0;
9
+ exports.matchSerialPorts = exports.listSerialPorts = exports.SlaveTransport = exports.MutexTransport = exports.TransportManager = exports.ModbusConnection = exports.isConnectionLostError = exports.normalizeModbusError = exports.RETRY_DELAY_MS = exports.MAX_RETRIES = exports.withRetry = exports.createModbusTransport = exports.createTCPTransport = exports.createRTUTransport = exports.createTransport = void 0;
10
10
  var factory_js_1 = require("./factory.js");
11
11
  Object.defineProperty(exports, "createTransport", { enumerable: true, get: function () { return factory_js_1.createTransport; } });
12
12
  var rtu_transport_js_1 = require("./rtu-transport.js");
@@ -30,4 +30,7 @@ var mutex_transport_js_1 = require("./mutex-transport.js");
30
30
  Object.defineProperty(exports, "MutexTransport", { enumerable: true, get: function () { return mutex_transport_js_1.MutexTransport; } });
31
31
  var slave_transport_js_1 = require("./slave-transport.js");
32
32
  Object.defineProperty(exports, "SlaveTransport", { enumerable: true, get: function () { return slave_transport_js_1.SlaveTransport; } });
33
+ var serial_ports_js_1 = require("./serial-ports.js");
34
+ Object.defineProperty(exports, "listSerialPorts", { enumerable: true, get: function () { return serial_ports_js_1.listSerialPorts; } });
35
+ Object.defineProperty(exports, "matchSerialPorts", { enumerable: true, get: function () { return serial_ports_js_1.matchSerialPorts; } });
33
36
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,2CAAoE;AAA3D,6GAAA,eAAe,OAAA;AACxB,uDAAuE;AAA9D,sHAAA,kBAAkB,OAAA;AAC3B,uDAAuE;AAA9D,sHAAA,kBAAkB,OAAA;AAC3B,2EAAoE;AAA3D,mIAAA,qBAAqB,OAAA;AAC9B,uCAAqF;AAA5E,qGAAA,SAAS,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,0GAAA,cAAc,OAAA;AAC/C,2DAA6E;AAApE,0HAAA,oBAAoB,OAAA;AAC7B,iDAA0F;AAAjF,sHAAA,qBAAqB,OAAA;AAAE,iHAAA,gBAAgB,OAAA;AAChD,2CAAkG;AAAzF,8GAAA,gBAAgB,OAAA;AACzB,2DAAqD;AAA5C,oHAAA,cAAc,OAAA;AACvB,2DAAqD;AAA5C,oHAAA,cAAc,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,2CAAoE;AAA3D,6GAAA,eAAe,OAAA;AACxB,uDAAuE;AAA9D,sHAAA,kBAAkB,OAAA;AAC3B,uDAAuE;AAA9D,sHAAA,kBAAkB,OAAA;AAC3B,2EAAoE;AAA3D,mIAAA,qBAAqB,OAAA;AAC9B,uCAAqF;AAA5E,qGAAA,SAAS,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,0GAAA,cAAc,OAAA;AAC/C,2DAA6E;AAApE,0HAAA,oBAAoB,OAAA;AAC7B,iDAA0F;AAAjF,sHAAA,qBAAqB,OAAA;AAAE,iHAAA,gBAAgB,OAAA;AAChD,2CAAkG;AAAzF,8GAAA,gBAAgB,OAAA;AACzB,2DAAqD;AAA5C,oHAAA,cAAc,OAAA;AACvB,2DAAqD;AAA5C,oHAAA,cAAc,OAAA;AACvB,qDAK0B;AAJxB,kHAAA,eAAe,OAAA;AACf,mHAAA,gBAAgB,OAAA"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * List serial ports with their USB identity and physical USB port, and select
3
+ * ports by these values
4
+ *
5
+ * USB serial adapters get a new device name when they are plugged in again
6
+ * (`/dev/ttyUSB0` becomes `/dev/ttyUSB1`). The USB vendor ID, product ID and
7
+ * serial number identify an adapter. Identical adapters without a serial number
8
+ * differ only in the physical USB port, which is the name of their link in
9
+ * `/dev/serial/by-path` (the location).
10
+ */
11
+ /** A serial port with its USB identity */
12
+ export interface SerialPortInfo {
13
+ /** Device path, for example `/dev/ttyUSB0` */
14
+ path: string;
15
+ /** USB vendor ID as 4 lowercase hex digits, for example `1a86` */
16
+ vendorId?: string;
17
+ /** USB product ID as 4 lowercase hex digits, for example `7523` */
18
+ productId?: string;
19
+ /** USB serial number, when the adapter has one */
20
+ serialNumber?: string;
21
+ /** Manufacturer name, when the adapter has one */
22
+ manufacturer?: string;
23
+ /**
24
+ * Names of the links in `/dev/serial/by-path` to this port, for example
25
+ * `pci-0000:00:1a.0-usb-0:1.3:1.0-port0`. Newer systemd versions add a
26
+ * second link (`...-usbv2-...`) for the same USB port. Empty when the port
27
+ * has no such link (not a USB adapter, or not Linux).
28
+ */
29
+ locations: string[];
30
+ }
31
+ /** Values to select ports by. Leave out `serialNumber` or `location` to match any. */
32
+ export interface SerialPortMatch {
33
+ /** USB vendor ID in hex, for example `1a86` or `0x1A86` */
34
+ vendorId: string;
35
+ /** USB product ID in hex, for example `7523` */
36
+ productId: string;
37
+ /** USB serial number (case-sensitive) */
38
+ serialNumber?: string;
39
+ /** Name of a link in `/dev/serial/by-path`, or the full path of the link */
40
+ location?: string;
41
+ }
42
+ /** A port as serialport's list or sysfs reports it */
43
+ export interface ListedSerialPort {
44
+ path: string;
45
+ vendorId?: string | undefined;
46
+ productId?: string | undefined;
47
+ serialNumber?: string | undefined;
48
+ manufacturer?: string | undefined;
49
+ }
50
+ /** System access of {@link listSerialPorts}, replaced in tests */
51
+ export interface SerialPortsDeps {
52
+ list: () => Promise<ListedSerialPort[]>;
53
+ readdir: (path: string) => Promise<string[]>;
54
+ realpath: (path: string) => Promise<string>;
55
+ readFile: (path: string) => Promise<string>;
56
+ }
57
+ /**
58
+ * List the serial ports with their USB identity and locations
59
+ *
60
+ * On Linux the ports are read from sysfs (`/sys/class/tty`). This starts no
61
+ * other program, so it works in a hardened service. Without sysfs (other
62
+ * systems), the port list of serialport is used (through modbus-serial).
63
+ *
64
+ * @param deps - Replacements for the system access, for tests
65
+ */
66
+ export declare function listSerialPorts(deps?: Partial<SerialPortsDeps>): Promise<SerialPortInfo[]>;
67
+ /**
68
+ * Select the ports that match all given values
69
+ *
70
+ * Returns every match, so the caller can tell a unique match from an
71
+ * ambiguous one. Ports without a USB vendor or product ID never match.
72
+ */
73
+ export declare function matchSerialPorts(ports: readonly SerialPortInfo[], match: SerialPortMatch): SerialPortInfo[];
74
+ //# sourceMappingURL=serial-ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serial-ports.d.ts","sourceRoot":"","sources":["../../../src/serial-ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;IAChB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAA;IACjB,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAClC;AAED,kEAAkE;AAClE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;IACvC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC5C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC5C;AAaD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,IAAI,GAAE,OAAO,CAAC,eAAe,CAAM,GAClC,OAAO,CAAC,cAAc,EAAE,CAAC,CAI3B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,KAAK,EAAE,eAAe,GACrB,cAAc,EAAE,CAelB"}
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ /**
3
+ * List serial ports with their USB identity and physical USB port, and select
4
+ * ports by these values
5
+ *
6
+ * USB serial adapters get a new device name when they are plugged in again
7
+ * (`/dev/ttyUSB0` becomes `/dev/ttyUSB1`). The USB vendor ID, product ID and
8
+ * serial number identify an adapter. Identical adapters without a serial number
9
+ * differ only in the physical USB port, which is the name of their link in
10
+ * `/dev/serial/by-path` (the location).
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.listSerialPorts = listSerialPorts;
14
+ exports.matchSerialPorts = matchSerialPorts;
15
+ const tslib_1 = require("tslib");
16
+ const promises_1 = require("node:fs/promises");
17
+ const node_path_1 = require("node:path");
18
+ const modbus_serial_1 = tslib_1.__importDefault(require("modbus-serial"));
19
+ const BY_PATH_DIR = '/dev/serial/by-path';
20
+ const SYS_TTY_DIR = '/sys/class/tty';
21
+ const systemDeps = {
22
+ // Loads the optional serialport package only when called
23
+ list: () => modbus_serial_1.default.getPorts(),
24
+ readdir: (path) => (0, promises_1.readdir)(path),
25
+ realpath: (path) => (0, promises_1.realpath)(path),
26
+ readFile: (path) => (0, promises_1.readFile)(path, 'utf8'),
27
+ };
28
+ /**
29
+ * List the serial ports with their USB identity and locations
30
+ *
31
+ * On Linux the ports are read from sysfs (`/sys/class/tty`). This starts no
32
+ * other program, so it works in a hardened service. Without sysfs (other
33
+ * systems), the port list of serialport is used (through modbus-serial).
34
+ *
35
+ * @param deps - Replacements for the system access, for tests
36
+ */
37
+ async function listSerialPorts(deps = {}) {
38
+ const system = { ...systemDeps, ...deps };
39
+ const [ports, locations] = await Promise.all([listPorts(system), readLocations(system)]);
40
+ return ports.map((port) => toPortInfo(port, locations.get(port.path) ?? []));
41
+ }
42
+ /**
43
+ * Select the ports that match all given values
44
+ *
45
+ * Returns every match, so the caller can tell a unique match from an
46
+ * ambiguous one. Ports without a USB vendor or product ID never match.
47
+ */
48
+ function matchSerialPorts(ports, match) {
49
+ const vendorId = normalizeUsbId(match.vendorId);
50
+ const productId = normalizeUsbId(match.productId);
51
+ const location = match.location?.startsWith(`${BY_PATH_DIR}/`)
52
+ ? match.location.slice(BY_PATH_DIR.length + 1)
53
+ : match.location;
54
+ return ports.filter((port) => vendorId !== undefined &&
55
+ port.vendorId === vendorId &&
56
+ productId !== undefined &&
57
+ port.productId === productId &&
58
+ (match.serialNumber === undefined || port.serialNumber === match.serialNumber) &&
59
+ (location === undefined || port.locations.includes(location)));
60
+ }
61
+ /** `1A86`, `0x1a86` and `1a86` become `1a86`; `403` becomes `0403` */
62
+ function normalizeUsbId(id) {
63
+ const hex = id?.match(/^(?:0x)?([0-9a-f]{1,4})$/i)?.[1];
64
+ return hex?.toLowerCase().padStart(4, '0');
65
+ }
66
+ function toPortInfo(port, locations) {
67
+ const vendorId = normalizeUsbId(port.vendorId);
68
+ const productId = normalizeUsbId(port.productId);
69
+ return {
70
+ path: port.path,
71
+ ...(vendorId ? { vendorId } : {}),
72
+ ...(productId ? { productId } : {}),
73
+ ...(port.serialNumber ? { serialNumber: port.serialNumber } : {}),
74
+ ...(port.manufacturer ? { manufacturer: port.manufacturer } : {}),
75
+ locations,
76
+ };
77
+ }
78
+ async function listPorts(system) {
79
+ try {
80
+ return await listSysfsPorts(system);
81
+ }
82
+ catch (error) {
83
+ if (!isNotFound(error))
84
+ throw error;
85
+ return system.list();
86
+ }
87
+ }
88
+ function isNotFound(error) {
89
+ return error.code === 'ENOENT';
90
+ }
91
+ /** Map each device path to the names of its links in /dev/serial/by-path */
92
+ async function readLocations(system) {
93
+ const locations = new Map();
94
+ let names;
95
+ try {
96
+ names = await system.readdir(BY_PATH_DIR);
97
+ }
98
+ catch (error) {
99
+ // udev creates the directory only while a USB serial adapter is plugged in
100
+ if (isNotFound(error))
101
+ return locations;
102
+ throw error;
103
+ }
104
+ const sorted = [...names].sort();
105
+ const targets = await Promise.all(sorted.map((name) =>
106
+ // A link points nowhere when its adapter is unplugged while listing
107
+ system.realpath((0, node_path_1.join)(BY_PATH_DIR, name)).catch(() => undefined)));
108
+ sorted.forEach((name, index) => {
109
+ const target = targets[index];
110
+ if (target !== undefined)
111
+ locations.set(target, [...(locations.get(target) ?? []), name]);
112
+ });
113
+ return locations;
114
+ }
115
+ /**
116
+ * List the ports in /sys/class/tty that belong to a device, and read the USB
117
+ * identity of the USB device above each one
118
+ */
119
+ async function listSysfsPorts(system) {
120
+ const names = await system.readdir(SYS_TTY_DIR);
121
+ const ports = await Promise.all(names.map(async (name) => {
122
+ // Virtual terminals (tty0, console, ptmx) have no device
123
+ const device = await system.realpath((0, node_path_1.join)(SYS_TTY_DIR, name, 'device')).catch(() => undefined);
124
+ return device === undefined
125
+ ? undefined
126
+ : { path: (0, node_path_1.join)('/dev', name), ...(await readUsbIdentity(system, device)) };
127
+ }));
128
+ return ports.filter((port) => port !== undefined);
129
+ }
130
+ /** Walk up from a tty device to the USB device, which has the idVendor file */
131
+ async function readUsbIdentity(system, device) {
132
+ for (let dir = device; dir !== (0, node_path_1.dirname)(dir); dir = (0, node_path_1.dirname)(dir)) {
133
+ const vendorId = await readAttribute(system, dir, 'idVendor');
134
+ if (vendorId === undefined)
135
+ continue;
136
+ return {
137
+ vendorId,
138
+ productId: await readAttribute(system, dir, 'idProduct'),
139
+ serialNumber: await readAttribute(system, dir, 'serial'),
140
+ manufacturer: await readAttribute(system, dir, 'manufacturer'),
141
+ };
142
+ }
143
+ return {};
144
+ }
145
+ /**
146
+ * Read a sysfs attribute without the newline that sysfs adds, or undefined
147
+ * when the device does not have it
148
+ */
149
+ function readAttribute(system, dir, name) {
150
+ return system.readFile((0, node_path_1.join)(dir, name)).then((value) => value.replace(/\n$/, ''), () => undefined);
151
+ }
152
+ //# sourceMappingURL=serial-ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serial-ports.js","sourceRoot":"","sources":["../../../src/serial-ports.ts"],"names":[],"mappings":"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"}