@slaplacian/tsups 0.1.0 → 0.1.1
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/package.json +1 -1
- package/src/serial.js +28 -1
package/package.json
CHANGED
package/src/serial.js
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
|
+
import { readdirSync } from 'node:fs';
|
|
1
2
|
import { SerialPort } from 'serialport';
|
|
2
3
|
import { BAUD_RATE, CMD, isInfoReply } from './protocol.js';
|
|
3
4
|
|
|
5
|
+
// USB serial devices by name. Used on Linux when SerialPort.list() cannot run:
|
|
6
|
+
// it shells out to udevadm, which minimal containers do not have.
|
|
7
|
+
export function scanDevPorts(dir = '/dev', names = readdirSync(dir)) {
|
|
8
|
+
return names
|
|
9
|
+
.filter((n) => /^tty(ACM|USB)\d+$/.test(n))
|
|
10
|
+
.sort()
|
|
11
|
+
.map((n) => ({ path: `${dir}/${n}` }));
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
export async function listPorts() {
|
|
5
|
-
|
|
15
|
+
try {
|
|
16
|
+
return await SerialPort.list();
|
|
17
|
+
} catch (err) {
|
|
18
|
+
if (process.platform !== 'linux') throw err;
|
|
19
|
+
return scanDevPorts();
|
|
20
|
+
}
|
|
6
21
|
}
|
|
7
22
|
|
|
23
|
+
export const isPermissionError = (err) => /permission denied|EACCES/i.test(`${err?.code} ${err?.message}`);
|
|
24
|
+
|
|
8
25
|
// The TS Shara USB interface (STM32 CDC, 0483:5740) holds each reply until the
|
|
9
26
|
// host writes again. A bare CR is ignored by the UPS but releases the reply.
|
|
10
27
|
const KICK = '\r';
|
|
@@ -127,8 +144,11 @@ export class UpsConnection {
|
|
|
127
144
|
}
|
|
128
145
|
|
|
129
146
|
// Probe: send "I", accept the port if the reply contains "TS".
|
|
147
|
+
// Throws when no UPS was found but some port could not be opened for lack of
|
|
148
|
+
// permission, since "not found" would hide the real cause.
|
|
130
149
|
export async function detectPort({ match = 'TS', timeout = 3000, log = () => {} } = {}) {
|
|
131
150
|
const ports = await listPorts();
|
|
151
|
+
const denied = [];
|
|
132
152
|
for (const info of ports) {
|
|
133
153
|
const lower = info.path.toLowerCase();
|
|
134
154
|
if (lower.includes('bluetooth') || lower.includes('blth')) continue;
|
|
@@ -139,9 +159,16 @@ export async function detectPort({ match = 'TS', timeout = 3000, log = () => {}
|
|
|
139
159
|
if (reply.includes(match)) return { path: info.path, info: reply };
|
|
140
160
|
} catch (err) {
|
|
141
161
|
log(`${info.path}: ${err.message}`);
|
|
162
|
+
if (isPermissionError(err)) denied.push(info.path);
|
|
142
163
|
} finally {
|
|
143
164
|
await conn.close();
|
|
144
165
|
}
|
|
145
166
|
}
|
|
167
|
+
if (denied.length) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`permission denied on ${denied.join(', ')}; add your user to the "dialout" group ` +
|
|
170
|
+
'(or "uucp" on Arch) and log in again, or run with sudo',
|
|
171
|
+
);
|
|
172
|
+
}
|
|
146
173
|
return null;
|
|
147
174
|
}
|