zenitel-client 0.1.1 → 0.1.2
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.js +82 -0
- package/dist/client.js +17 -34
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -297,6 +297,83 @@ Optional:
|
|
|
297
297
|
console.log(` After reboot, call button will dial: ${agentNumber}`);
|
|
298
298
|
break;
|
|
299
299
|
}
|
|
300
|
+
case 'audio': {
|
|
301
|
+
const client = getClient();
|
|
302
|
+
const subCmd = args[1]; // get | set | backup
|
|
303
|
+
if (subCmd === 'set') {
|
|
304
|
+
const partial = {};
|
|
305
|
+
if (flag('speaker') !== undefined)
|
|
306
|
+
partial.speaker = { gain: Number(flag('speaker')) };
|
|
307
|
+
if (flag('mic') !== undefined)
|
|
308
|
+
partial.mic = { gain: Number(flag('mic')) };
|
|
309
|
+
if (hasFlag('aec-on'))
|
|
310
|
+
partial.aec = { enabled: true };
|
|
311
|
+
if (hasFlag('aec-off'))
|
|
312
|
+
partial.aec = { enabled: false };
|
|
313
|
+
if (hasFlag('anc-on'))
|
|
314
|
+
partial.anc = { enabled: true };
|
|
315
|
+
if (hasFlag('anc-off'))
|
|
316
|
+
partial.anc = { enabled: false };
|
|
317
|
+
if (hasFlag('drc-on'))
|
|
318
|
+
partial.drc = { enabled: true, gain: Number(flag('drc-gain') ?? 8) };
|
|
319
|
+
if (hasFlag('drc-off'))
|
|
320
|
+
partial.drc = { enabled: false };
|
|
321
|
+
if (hasFlag('avc-on'))
|
|
322
|
+
partial.avc = { enabled: true };
|
|
323
|
+
if (hasFlag('avc-off'))
|
|
324
|
+
partial.avc = { enabled: false };
|
|
325
|
+
if (Object.keys(partial).length === 0) {
|
|
326
|
+
console.error(`Usage: zenitel audio set -h <host> [options]
|
|
327
|
+
|
|
328
|
+
--speaker <dB> Speaker gain (-10 to +13)
|
|
329
|
+
--mic <dB> Mic gain (-10 to +10)
|
|
330
|
+
--aec-on/off Echo cancellation
|
|
331
|
+
--anc-on/off Noise suppression
|
|
332
|
+
--drc-on/off Dynamic compression (--drc-gain <0-20>)
|
|
333
|
+
--avc-on/off Auto volume`);
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
console.log('🔧 Updating audio settings...');
|
|
337
|
+
await client.setAudioSettings(partial);
|
|
338
|
+
console.log('✅ Audio settings applied.');
|
|
339
|
+
// Show updated values
|
|
340
|
+
const after = await client.getAudioSettings();
|
|
341
|
+
if (partial.speaker)
|
|
342
|
+
console.log(` Speaker: ${after.speaker.gain} dB`);
|
|
343
|
+
if (partial.mic)
|
|
344
|
+
console.log(` Mic: ${after.mic.gain} dB`);
|
|
345
|
+
if (partial.aec)
|
|
346
|
+
console.log(` AEC: ${after.aec.enabled ? 'ON' : 'OFF'}`);
|
|
347
|
+
if (partial.anc)
|
|
348
|
+
console.log(` ANC: ${after.anc.enabled ? 'ON' : 'OFF'}`);
|
|
349
|
+
if (partial.drc)
|
|
350
|
+
console.log(` DRC: ${after.drc.enabled ? 'ON' : 'OFF'} (${after.drc.gain} dBA)`);
|
|
351
|
+
if (partial.avc)
|
|
352
|
+
console.log(` AVC: ${after.avc.enabled ? 'ON' : 'OFF'}`);
|
|
353
|
+
}
|
|
354
|
+
else if (subCmd === 'backup') {
|
|
355
|
+
const { writeFileSync } = await import('node:fs');
|
|
356
|
+
const outFile = flag('out', 'o') || 'audio-backup.json';
|
|
357
|
+
console.log('💾 Backing up audio config...');
|
|
358
|
+
const raw = await client.getAudioSettingsRaw();
|
|
359
|
+
writeFileSync(outFile, JSON.stringify(raw, null, 2));
|
|
360
|
+
console.log(`✅ Saved to ${outFile}`);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
// Default: get / show
|
|
364
|
+
console.log(`🎵 Audio settings for ${flag('host', 'h')}:\n`);
|
|
365
|
+
const a = await client.getAudioSettings();
|
|
366
|
+
console.log(` Speaker: ${a.speaker.gain > 0 ? '+' : ''}${a.speaker.gain} dB (range: -10 to +13)`);
|
|
367
|
+
console.log(` Mic: ${a.mic.gain > 0 ? '+' : ''}${a.mic.gain} dB (range: -10 to +10)`);
|
|
368
|
+
console.log(` AEC: ${a.aec.enabled ? '✅ ON' : '❌ OFF'} (${a.aec.mode})`);
|
|
369
|
+
console.log(` ANC: ${a.anc.enabled ? '✅ ON' : '❌ OFF'} (${a.anc.mode})`);
|
|
370
|
+
console.log(` DRC: ${a.drc.enabled ? '✅ ON' : '❌ OFF'} (${a.drc.gain} dBA)`);
|
|
371
|
+
console.log(` AVC: ${a.avc.enabled ? '✅ ON' : '❌ OFF'}`);
|
|
372
|
+
console.log(` FESS: ${a.fess.enabled ? '✅ ON' : '❌ OFF'} (threshold: ${a.fess.threshold} dBFS)`);
|
|
373
|
+
console.log(` Mode: ${a.mode}`);
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
300
377
|
default:
|
|
301
378
|
console.log(`zenitel — Zenitel intercom CLI
|
|
302
379
|
|
|
@@ -312,6 +389,9 @@ Commands:
|
|
|
312
389
|
sip set -h <host> Write SIP config (--domain --number --proxy ...)
|
|
313
390
|
webcall enable -h <host> Enable webcall + relay HTTP API
|
|
314
391
|
webcall disable -h <host> Disable webcall + relay HTTP API
|
|
392
|
+
audio -h <host> Read audio settings
|
|
393
|
+
audio set -h <host> Write audio (--speaker --mic --aec-on/off ...)
|
|
394
|
+
audio backup -h <host> Backup audio config to JSON
|
|
315
395
|
backup [file] -h <host> Download config as tar.gz
|
|
316
396
|
restore <file> -h <host> Upload config tar.gz
|
|
317
397
|
reboot -h <host> Reboot the device
|
|
@@ -329,6 +409,8 @@ Options:
|
|
|
329
409
|
--number SIP directory number
|
|
330
410
|
--proxy Outbound proxy
|
|
331
411
|
--transport SIP transport (udp/tcp/tls)
|
|
412
|
+
--speaker Speaker gain in dB (-10 to +13)
|
|
413
|
+
--mic Mic gain in dB (-10 to +10)
|
|
332
414
|
--name Display name`);
|
|
333
415
|
}
|
|
334
416
|
}
|
package/dist/client.js
CHANGED
|
@@ -468,41 +468,24 @@ export class ZenitelClient {
|
|
|
468
468
|
}
|
|
469
469
|
/** Get raw audio config JSON from the device (for backup/debug) */
|
|
470
470
|
async getAudioSettingsRaw() {
|
|
471
|
-
// The audio
|
|
472
|
-
//
|
|
473
|
-
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
471
|
+
// The AngularJS audio controller fetches data via:
|
|
472
|
+
// POST /goform/zForm_auto_config
|
|
473
|
+
// body: get=get&path=/state/config/audio
|
|
474
|
+
// Response: { out: { get: { data: { audio: {...} } } } }
|
|
475
|
+
const body = new URLSearchParams({
|
|
476
|
+
get: 'get',
|
|
477
|
+
path: '/state/config/audio',
|
|
478
|
+
}).toString();
|
|
479
|
+
const res = await this._fetch('/goform/zForm_auto_config', 'POST', undefined, body, 'application/x-www-form-urlencoded');
|
|
480
|
+
// Zenitel appends a trailing form-feed (\f) after JSON — can't use res.json()
|
|
481
|
+
const text = (await res.text()).trim();
|
|
482
|
+
const json = JSON.parse(text);
|
|
483
|
+
// Response shape: { out: { get: { data: { audio: {...} } } } }
|
|
484
|
+
const audio = json?.out?.get?.data?.audio;
|
|
485
|
+
if (!audio) {
|
|
486
|
+
throw new Error('Unexpected response from audio config endpoint. Check firmware compatibility.');
|
|
482
487
|
}
|
|
483
|
-
|
|
484
|
-
// Strategy 2: POST to get the JSON directly (some FW versions)
|
|
485
|
-
const res = await this._fetch('/goform/zForm_auto_config', 'GET');
|
|
486
|
-
const text = await res.text();
|
|
487
|
-
// The response may be JSON directly
|
|
488
|
-
try {
|
|
489
|
-
const parsed = JSON.parse(text);
|
|
490
|
-
if (parsed.audio)
|
|
491
|
-
return parsed;
|
|
492
|
-
}
|
|
493
|
-
catch { /* not JSON */ }
|
|
494
|
-
// Strategy 3: Look for hidden input or textarea with JSON
|
|
495
|
-
const inputMatch = html.match(/value='(\{"audio"[^']*)'/);
|
|
496
|
-
if (inputMatch) {
|
|
497
|
-
jsonStr = inputMatch[1]
|
|
498
|
-
.replace(/"/g, '"')
|
|
499
|
-
.replace(/&/g, '&');
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
if (!jsonStr) {
|
|
503
|
-
throw new Error('Could not extract audio config JSON from device. Firmware may not support this endpoint.');
|
|
504
|
-
}
|
|
505
|
-
return JSON.parse(jsonStr);
|
|
488
|
+
return { audio };
|
|
506
489
|
}
|
|
507
490
|
/** Parse the raw goform JSON into our typed AudioSettings */
|
|
508
491
|
_parseAudioJson(raw) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zenitel-client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "HTTP client + network scanner for Zenitel intercom systems (TCIV-2+, TCIV-3). Control relays, SIP configuration, DAK provisioning, webcall, and camera feeds.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|