@agentdeck/bridge 1.0.3 → 1.0.4
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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +139 -69
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +5 -18
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-discover.js +6 -14
- package/dist/idotmatrix/idotmatrix-discover.js.map +1 -1
- package/dist/python-ble-runtime.d.ts +62 -0
- package/dist/python-ble-runtime.d.ts.map +1 -0
- package/dist/python-ble-runtime.js +215 -0
- package/dist/python-ble-runtime.js.map +1 -0
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -1
- package/dist/timebox/timebox-daemon-sync.js +5 -20
- package/dist/timebox/timebox-daemon-sync.js.map +1 -1
- package/dist/timebox/timebox-discover.d.ts.map +1 -1
- package/dist/timebox/timebox-discover.js +6 -15
- package/dist/timebox/timebox-discover.js.map +1 -1
- package/package.json +8 -4
- package/python/requirements-ble.txt +5 -0
- package/src/idotmatrix/brightness.py +40 -0
- package/src/idotmatrix/scan.py +35 -0
- package/src/idotmatrix/sync.py +400 -0
- package/src/pysync/matrix_sync_common.py +89 -0
- package/src/timebox/scan_ble.py +52 -0
- package/src/timebox/sync_ble.py +394 -0
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+MpC,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAM,CAAC;AAOtF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AAoBD,QAAA,MAAM,OAAO,SAAgB,CAAC;AA6iE9B,wBAAsB,MAAM,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzE;AAED,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'url';
|
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
9
|
import { request } from 'http';
|
|
10
10
|
import { BRIDGE_WS_PORT } from './types.js';
|
|
11
|
+
import { ensureBleRuntime, getBleRuntimeStatus } from './python-ble-runtime.js';
|
|
11
12
|
import { TASK_NAME, installWindowsTask, taskExists, runWindowsTask, endWindowsTask, deleteWindowsTask, } from './windows-service.js';
|
|
12
13
|
const require = createRequire(import.meta.url);
|
|
13
14
|
const packageJson = require('../package.json');
|
|
@@ -135,19 +136,30 @@ async function isDaemonPort(port) {
|
|
|
135
136
|
return false;
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
|
-
function resolveTimeboxSyncPaths() {
|
|
139
|
-
const distPath = dirname(fileURLToPath(import.meta.url));
|
|
140
|
-
const projectRoot = join(distPath, '..', '..');
|
|
141
|
-
const timeboxDir = join(projectRoot, 'bridge', 'src', 'timebox');
|
|
142
|
-
return {
|
|
143
|
-
python: join(projectRoot, '.venv', 'bin', 'python'),
|
|
144
|
-
bleScript: join(timeboxDir, 'sync_ble.py'),
|
|
145
|
-
scanScript: join(timeboxDir, 'scan_ble.py'),
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
139
|
function projectRootPath() {
|
|
149
140
|
return join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
150
141
|
}
|
|
142
|
+
async function runBlePython(python, args, captureStdout = false) {
|
|
143
|
+
return new Promise((resolve, reject) => {
|
|
144
|
+
const child = spawn(python, args, captureStdout ? { stdio: ['ignore', 'pipe', 'inherit'] } : { stdio: 'inherit' });
|
|
145
|
+
let stdout = '';
|
|
146
|
+
child.stdout?.on('data', (data) => {
|
|
147
|
+
stdout += data.toString();
|
|
148
|
+
});
|
|
149
|
+
child.once('error', reject);
|
|
150
|
+
child.once('close', (code) => resolve({ code, stdout }));
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function prepareBleRuntime() {
|
|
154
|
+
try {
|
|
155
|
+
return ensureBleRuntime({ log });
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
log(`BLE support unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
151
163
|
const ESP32_OTA_BOARDS = [
|
|
152
164
|
{ board: 'inkdeck', env: 'inkdeck', aliases: [] },
|
|
153
165
|
{ board: 'ulanzi_tc001', env: 'led8x32', aliases: ['led8x32'] },
|
|
@@ -1149,35 +1161,51 @@ task
|
|
|
1149
1161
|
.action(async (opts) => {
|
|
1150
1162
|
await postTaskClose({ signal: 'manual', outcome: 'abandoned', sessionId: opts.session });
|
|
1151
1163
|
});
|
|
1164
|
+
// ===== Optional Python BLE runtime =====
|
|
1165
|
+
const ble = program.command('ble').description('Prepare optional Python support for BLE display devices');
|
|
1166
|
+
ble
|
|
1167
|
+
.command('setup')
|
|
1168
|
+
.description('Create the private BLE environment and install its Python dependencies')
|
|
1169
|
+
.action(() => {
|
|
1170
|
+
const runtime = prepareBleRuntime();
|
|
1171
|
+
if (runtime)
|
|
1172
|
+
log(`BLE support ready (${runtime.python}).`);
|
|
1173
|
+
});
|
|
1174
|
+
ble
|
|
1175
|
+
.command('status')
|
|
1176
|
+
.description('Show whether optional BLE support is ready')
|
|
1177
|
+
.action(() => {
|
|
1178
|
+
const status = getBleRuntimeStatus();
|
|
1179
|
+
if (status.ready) {
|
|
1180
|
+
log(`BLE support ready (${status.python}).`);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
log(`BLE support is not ready: ${status.reason}. Run \`agentdeck ble setup\`.`);
|
|
1184
|
+
process.exitCode = 1;
|
|
1185
|
+
});
|
|
1152
1186
|
// ===== iDotMatrix commands =====
|
|
1153
1187
|
const idotmatrix = program.command('idotmatrix').description('Manage iDotMatrix BLE pixel display devices');
|
|
1154
1188
|
idotmatrix
|
|
1155
1189
|
.command('scan')
|
|
1156
1190
|
.description('Discover iDotMatrix devices via BLE')
|
|
1157
1191
|
.action(async () => {
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
const distPath = dirname(fileURLToPath(import.meta.url));
|
|
1162
|
-
const projectRoot = join(distPath, '..', '..');
|
|
1163
|
-
const venvPython = join(projectRoot, '.venv', 'bin', 'python');
|
|
1164
|
-
const scanScript = join(projectRoot, 'bridge', 'src', 'idotmatrix', 'scan.py');
|
|
1192
|
+
const runtime = prepareBleRuntime();
|
|
1193
|
+
if (!runtime)
|
|
1194
|
+
return;
|
|
1165
1195
|
log('Scanning for BLE devices (5 seconds)...');
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
py.stdout.on('data', (data) => {
|
|
1169
|
-
stdoutData += data.toString();
|
|
1170
|
-
});
|
|
1171
|
-
py.on('close', (code) => {
|
|
1196
|
+
try {
|
|
1197
|
+
const { code, stdout } = await runBlePython(runtime.python, [runtime.paths.scripts.idotmatrixScan], true);
|
|
1172
1198
|
if (code !== 0) {
|
|
1173
1199
|
log('Failed to run scan script.');
|
|
1174
|
-
process.
|
|
1200
|
+
process.exitCode = 1;
|
|
1201
|
+
return;
|
|
1175
1202
|
}
|
|
1176
1203
|
try {
|
|
1177
|
-
const devices = JSON.parse(
|
|
1204
|
+
const devices = JSON.parse(stdout.trim());
|
|
1178
1205
|
if (devices.error) {
|
|
1179
1206
|
log(`Scan error: ${devices.error}`);
|
|
1180
|
-
process.
|
|
1207
|
+
process.exitCode = 1;
|
|
1208
|
+
return;
|
|
1181
1209
|
}
|
|
1182
1210
|
if (!Array.isArray(devices) || devices.length === 0) {
|
|
1183
1211
|
log('No BLE devices found.');
|
|
@@ -1189,10 +1217,15 @@ idotmatrix
|
|
|
1189
1217
|
log(`${prefix}${d.name} (${d.address})`);
|
|
1190
1218
|
}
|
|
1191
1219
|
}
|
|
1192
|
-
catch
|
|
1193
|
-
log(`Failed to parse scan output: ${
|
|
1220
|
+
catch {
|
|
1221
|
+
log(`Failed to parse scan output: ${stdout}`);
|
|
1222
|
+
process.exitCode = 1;
|
|
1194
1223
|
}
|
|
1195
|
-
}
|
|
1224
|
+
}
|
|
1225
|
+
catch (err) {
|
|
1226
|
+
log(`Failed to start BLE scan: ${err instanceof Error ? err.message : String(err)}`);
|
|
1227
|
+
process.exitCode = 1;
|
|
1228
|
+
}
|
|
1196
1229
|
});
|
|
1197
1230
|
idotmatrix
|
|
1198
1231
|
.command('add <address>')
|
|
@@ -1243,9 +1276,6 @@ idotmatrix
|
|
|
1243
1276
|
.option('-a, --address <address>', 'BLE Address (defaults to first configured device)')
|
|
1244
1277
|
.action(async (valueStr, opts) => {
|
|
1245
1278
|
const { loadIDotMatrixDevices } = await import('./idotmatrix/idotmatrix-settings.js');
|
|
1246
|
-
const { dirname } = await import('path');
|
|
1247
|
-
const { fileURLToPath } = await import('url');
|
|
1248
|
-
const { spawn } = await import('child_process');
|
|
1249
1279
|
const value = parseInt(valueStr, 10);
|
|
1250
1280
|
if (isNaN(value) || value < 5 || value > 100) {
|
|
1251
1281
|
log('Brightness value must be between 5 and 100.');
|
|
@@ -1260,22 +1290,30 @@ idotmatrix
|
|
|
1260
1290
|
}
|
|
1261
1291
|
targetAddress = devices[0].address;
|
|
1262
1292
|
}
|
|
1263
|
-
const
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
const brightnessScript = join(projectRoot, 'bridge', 'src', 'idotmatrix', 'brightness.py');
|
|
1293
|
+
const runtime = prepareBleRuntime();
|
|
1294
|
+
if (!runtime)
|
|
1295
|
+
return;
|
|
1267
1296
|
log(`Setting brightness of ${targetAddress} to ${value}%...`);
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1297
|
+
try {
|
|
1298
|
+
const { code } = await runBlePython(runtime.python, [
|
|
1299
|
+
runtime.paths.scripts.idotmatrixBrightness,
|
|
1300
|
+
'-a',
|
|
1301
|
+
targetAddress,
|
|
1302
|
+
'-b',
|
|
1303
|
+
String(value),
|
|
1304
|
+
]);
|
|
1272
1305
|
if (code === 0) {
|
|
1273
1306
|
log('Brightness updated successfully.');
|
|
1274
1307
|
}
|
|
1275
1308
|
else {
|
|
1276
1309
|
log(`Failed to update brightness, process exited with code ${code}`);
|
|
1310
|
+
process.exitCode = code ?? 1;
|
|
1277
1311
|
}
|
|
1278
|
-
}
|
|
1312
|
+
}
|
|
1313
|
+
catch (err) {
|
|
1314
|
+
log(`Failed to start brightness command: ${err instanceof Error ? err.message : String(err)}`);
|
|
1315
|
+
process.exitCode = 1;
|
|
1316
|
+
}
|
|
1279
1317
|
});
|
|
1280
1318
|
idotmatrix
|
|
1281
1319
|
.command('sync [address]')
|
|
@@ -1286,9 +1324,6 @@ idotmatrix
|
|
|
1286
1324
|
.action(async (addressOpt, opts) => {
|
|
1287
1325
|
const { loadIDotMatrixDevices } = await import('./idotmatrix/idotmatrix-settings.js');
|
|
1288
1326
|
const { readDaemonInfo, findDaemonPort } = await import('./session-registry.js');
|
|
1289
|
-
const { dirname } = await import('path');
|
|
1290
|
-
const { fileURLToPath } = await import('url');
|
|
1291
|
-
const { spawn } = await import('child_process');
|
|
1292
1327
|
let targetAddress = addressOpt;
|
|
1293
1328
|
let defaultBrightness = 100;
|
|
1294
1329
|
const devices = loadIDotMatrixDevices();
|
|
@@ -1314,17 +1349,30 @@ idotmatrix
|
|
|
1314
1349
|
? parseInt(opts.port, 10)
|
|
1315
1350
|
: (info?.httpPort ?? info?.port ?? findDaemonPort() ?? BRIDGE_WS_PORT);
|
|
1316
1351
|
const url = `http://127.0.0.1:${port}`;
|
|
1317
|
-
const
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
const syncScript = join(projectRoot, 'bridge', 'src', 'idotmatrix', 'sync.py');
|
|
1352
|
+
const runtime = prepareBleRuntime();
|
|
1353
|
+
if (!runtime)
|
|
1354
|
+
return;
|
|
1321
1355
|
log(`Starting BLE sync client linking to bridge at ${url} (brightness ${brightness}%, boost ${boost}x)...`);
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1356
|
+
try {
|
|
1357
|
+
const { code } = await runBlePython(runtime.python, [
|
|
1358
|
+
runtime.paths.scripts.idotmatrixSync,
|
|
1359
|
+
'-a',
|
|
1360
|
+
targetAddress,
|
|
1361
|
+
'-u',
|
|
1362
|
+
url,
|
|
1363
|
+
'-b',
|
|
1364
|
+
String(brightness),
|
|
1365
|
+
'--boost',
|
|
1366
|
+
String(boost),
|
|
1367
|
+
]);
|
|
1326
1368
|
log(`Sync process exited with code ${code}`);
|
|
1327
|
-
|
|
1369
|
+
if (code !== 0)
|
|
1370
|
+
process.exitCode = code ?? 1;
|
|
1371
|
+
}
|
|
1372
|
+
catch (err) {
|
|
1373
|
+
log(`Failed to start BLE sync: ${err instanceof Error ? err.message : String(err)}`);
|
|
1374
|
+
process.exitCode = 1;
|
|
1375
|
+
}
|
|
1328
1376
|
});
|
|
1329
1377
|
// ===== Divoom Timebox Mini Light commands =====
|
|
1330
1378
|
const timebox = program.command('timebox').description('Manage Divoom Timebox Mini devices (Bluetooth LE)');
|
|
@@ -1332,22 +1380,22 @@ timebox
|
|
|
1332
1380
|
.command('scan')
|
|
1333
1381
|
.description('Discover BLE TimeBox-mini-light peripherals')
|
|
1334
1382
|
.action(async () => {
|
|
1335
|
-
const
|
|
1383
|
+
const runtime = prepareBleRuntime();
|
|
1384
|
+
if (!runtime)
|
|
1385
|
+
return;
|
|
1336
1386
|
log('Scanning for BLE devices (5 seconds)...');
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
py.stdout.on('data', (data) => {
|
|
1340
|
-
stdoutData += data.toString();
|
|
1341
|
-
});
|
|
1342
|
-
py.on('close', (code) => {
|
|
1387
|
+
try {
|
|
1388
|
+
const { code, stdout } = await runBlePython(runtime.python, [runtime.paths.scripts.timeboxScan], true);
|
|
1343
1389
|
if (code !== 0) {
|
|
1344
|
-
log('BLE scan failed
|
|
1390
|
+
log('BLE scan failed.');
|
|
1391
|
+
process.exitCode = 1;
|
|
1345
1392
|
return;
|
|
1346
1393
|
}
|
|
1347
1394
|
try {
|
|
1348
|
-
const devices = JSON.parse(
|
|
1395
|
+
const devices = JSON.parse(stdout.trim());
|
|
1349
1396
|
if (devices.error) {
|
|
1350
1397
|
log(`BLE scan error: ${devices.error}`);
|
|
1398
|
+
process.exitCode = 1;
|
|
1351
1399
|
return;
|
|
1352
1400
|
}
|
|
1353
1401
|
if (!Array.isArray(devices) || devices.length === 0) {
|
|
@@ -1363,9 +1411,14 @@ timebox
|
|
|
1363
1411
|
log('\nAdd the starred BLE device with: agentdeck timebox add <address>');
|
|
1364
1412
|
}
|
|
1365
1413
|
catch {
|
|
1366
|
-
log(`Failed to parse BLE scan output: ${
|
|
1414
|
+
log(`Failed to parse BLE scan output: ${stdout}`);
|
|
1415
|
+
process.exitCode = 1;
|
|
1367
1416
|
}
|
|
1368
|
-
}
|
|
1417
|
+
}
|
|
1418
|
+
catch (err) {
|
|
1419
|
+
log(`Failed to start BLE scan: ${err instanceof Error ? err.message : String(err)}`);
|
|
1420
|
+
process.exitCode = 1;
|
|
1421
|
+
}
|
|
1369
1422
|
});
|
|
1370
1423
|
timebox
|
|
1371
1424
|
.command('add <address>')
|
|
@@ -1442,11 +1495,22 @@ async function runTimeboxSync(targetOpt, opts, once) {
|
|
|
1442
1495
|
? parseInt(opts.bridgePort, 10)
|
|
1443
1496
|
: (info?.httpPort ?? info?.port ?? findDaemonPort() ?? BRIDGE_WS_PORT);
|
|
1444
1497
|
const url = `http://127.0.0.1:${bridgePort}`;
|
|
1445
|
-
const
|
|
1446
|
-
|
|
1498
|
+
const runtime = prepareBleRuntime();
|
|
1499
|
+
if (!runtime)
|
|
1500
|
+
return;
|
|
1501
|
+
const args = [
|
|
1502
|
+
runtime.paths.scripts.timeboxSync,
|
|
1503
|
+
'--address',
|
|
1504
|
+
id,
|
|
1505
|
+
'--url',
|
|
1506
|
+
url,
|
|
1507
|
+
'--brightness',
|
|
1508
|
+
String(brightness),
|
|
1509
|
+
...(once ? ['--once'] : []),
|
|
1510
|
+
];
|
|
1447
1511
|
log(`${once ? 'Sending one Timebox frame' : 'Starting Timebox sync'} via BLE ${id} from ${url} (brightness ${brightness}%)...`);
|
|
1448
|
-
|
|
1449
|
-
|
|
1512
|
+
try {
|
|
1513
|
+
const { code } = await runBlePython(runtime.python, args);
|
|
1450
1514
|
if (once) {
|
|
1451
1515
|
if (code === 0) {
|
|
1452
1516
|
log('Timebox test frame sent.');
|
|
@@ -1458,8 +1522,14 @@ async function runTimeboxSync(targetOpt, opts, once) {
|
|
|
1458
1522
|
}
|
|
1459
1523
|
else {
|
|
1460
1524
|
log(`Timebox sync process exited with code ${code}`);
|
|
1525
|
+
if (code !== 0)
|
|
1526
|
+
process.exitCode = code ?? 1;
|
|
1461
1527
|
}
|
|
1462
|
-
}
|
|
1528
|
+
}
|
|
1529
|
+
catch (err) {
|
|
1530
|
+
log(`Failed to start Timebox BLE command: ${err instanceof Error ? err.message : String(err)}`);
|
|
1531
|
+
process.exitCode = 1;
|
|
1532
|
+
}
|
|
1463
1533
|
}
|
|
1464
1534
|
timebox
|
|
1465
1535
|
.command('test [target]')
|