@iobroker/assistant-satellite 0.0.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/LICENSE +21 -0
- package/README.md +173 -0
- package/build/audio.d.ts +20 -0
- package/build/audio.js +127 -0
- package/build/audio.js.map +1 -0
- package/build/checks.d.ts +13 -0
- package/build/checks.js +150 -0
- package/build/checks.js.map +1 -0
- package/build/cli.d.ts +2 -0
- package/build/cli.js +134 -0
- package/build/cli.js.map +1 -0
- package/build/config.d.ts +46 -0
- package/build/config.js +36 -0
- package/build/config.js.map +1 -0
- package/build/index.d.ts +21 -0
- package/build/index.js +9 -0
- package/build/index.js.map +1 -0
- package/build/models.d.ts +15 -0
- package/build/models.js +101 -0
- package/build/models.js.map +1 -0
- package/build/mqtt.d.ts +6 -0
- package/build/mqtt.js +47 -0
- package/build/mqtt.js.map +1 -0
- package/build/protocol.d.ts +54 -0
- package/build/protocol.js +35 -0
- package/build/protocol.js.map +1 -0
- package/build/satellite.d.ts +54 -0
- package/build/satellite.js +384 -0
- package/build/satellite.js.map +1 -0
- package/build/service.d.ts +3 -0
- package/build/service.js +140 -0
- package/build/service.js.map +1 -0
- package/build/vad.d.ts +19 -0
- package/build/vad.js +55 -0
- package/build/vad.js.map +1 -0
- package/build/wakeword.d.ts +29 -0
- package/build/wakeword.js +179 -0
- package/build/wakeword.js.map +1 -0
- package/config.example.json +23 -0
- package/package.json +35 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.Satellite = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* Satellite orchestration: resolve the adapter address → register → listen for the wake word →
|
|
39
|
+
* stream the utterance → play the spoken reply. Protocol-compatible with the Hannah satellite.
|
|
40
|
+
*/
|
|
41
|
+
const dgram = __importStar(require("node:dgram"));
|
|
42
|
+
const protocol_1 = require("./protocol");
|
|
43
|
+
const audio_1 = require("./audio");
|
|
44
|
+
const vad_1 = require("./vad");
|
|
45
|
+
const wakeword_1 = require("./wakeword");
|
|
46
|
+
const models_1 = require("./models");
|
|
47
|
+
const mqtt_1 = require("./mqtt");
|
|
48
|
+
/** 1280 samples (80 ms) of 16 kHz mono 16-bit PCM per processing frame. */
|
|
49
|
+
const FRAME_BYTES = 1280 * 2;
|
|
50
|
+
const FRAME_MS = 80;
|
|
51
|
+
/** Missed heartbeat ACKs before the satellite assumes the adapter is gone and re-registers. */
|
|
52
|
+
const MAX_HEARTBEAT_MISSES = 3;
|
|
53
|
+
const RECONNECT_MAX_BACKOFF_MS = 30_000;
|
|
54
|
+
const sleep = (ms) => new Promise(res => setTimeout(res, ms));
|
|
55
|
+
class Satellite {
|
|
56
|
+
cfg;
|
|
57
|
+
host;
|
|
58
|
+
log;
|
|
59
|
+
socket = null;
|
|
60
|
+
serverHost = '';
|
|
61
|
+
serverPort = 0;
|
|
62
|
+
wakeword;
|
|
63
|
+
mic = null;
|
|
64
|
+
backend;
|
|
65
|
+
plingPcm = Buffer.alloc(0);
|
|
66
|
+
heartbeatTimer = null;
|
|
67
|
+
// mic frame assembly
|
|
68
|
+
micRemainder = Buffer.alloc(0);
|
|
69
|
+
pumping = false;
|
|
70
|
+
micBytesLogged = false;
|
|
71
|
+
// wake-word diagnostics (periodic summary)
|
|
72
|
+
wakeFrames = 0;
|
|
73
|
+
wakeMaxScore = 0;
|
|
74
|
+
wakeMaxRms = 0;
|
|
75
|
+
// recording state
|
|
76
|
+
recording = false;
|
|
77
|
+
silence = null;
|
|
78
|
+
preBuffer = [];
|
|
79
|
+
recFrames = 0;
|
|
80
|
+
recPeakRms = 0;
|
|
81
|
+
// tts receiver
|
|
82
|
+
ttsChunks = [];
|
|
83
|
+
ttsDiscard = false;
|
|
84
|
+
registerResolve = null;
|
|
85
|
+
running = false;
|
|
86
|
+
heartbeatMisses = 0;
|
|
87
|
+
awaitingHeartbeatAck = false;
|
|
88
|
+
reconnecting = false;
|
|
89
|
+
constructor(cfg, host) {
|
|
90
|
+
this.cfg = cfg;
|
|
91
|
+
this.host = host;
|
|
92
|
+
this.log = host.log;
|
|
93
|
+
this.backend = (0, audio_1.resolveBackend)(cfg.audioBackend);
|
|
94
|
+
}
|
|
95
|
+
async start() {
|
|
96
|
+
this.running = true;
|
|
97
|
+
const addr = await this.resolveAddress();
|
|
98
|
+
this.serverHost = addr.host;
|
|
99
|
+
this.serverPort = addr.port;
|
|
100
|
+
this.log.info(`Adapter address: ${this.serverHost}:${this.serverPort}`);
|
|
101
|
+
await this.openSocket();
|
|
102
|
+
const models = await (0, models_1.ensureModels)(this.cfg.modelsDir, this.cfg.wakewordModel, this.log);
|
|
103
|
+
this.wakeword = new wakeword_1.WakeWord(models, this.cfg.wakewordThreshold, this.log);
|
|
104
|
+
await this.wakeword.load();
|
|
105
|
+
this.plingPcm = (0, audio_1.pling)();
|
|
106
|
+
// Retry until the adapter answers, so the satellite survives the adapter being down at boot.
|
|
107
|
+
await this.registerWithRetry();
|
|
108
|
+
this.heartbeatTimer = setInterval(() => this.heartbeatTick(), this.cfg.heartbeatIntervalMs);
|
|
109
|
+
this.mic = new audio_1.Mic(this.backend, this.cfg.micDevice, this.log);
|
|
110
|
+
this.mic.start(d => this.onMicData(d));
|
|
111
|
+
this.setStatus('idle');
|
|
112
|
+
this.log.info(`Satellite '${this.cfg.device}' ready. Listening for the wake word …`);
|
|
113
|
+
}
|
|
114
|
+
async stop() {
|
|
115
|
+
this.running = false;
|
|
116
|
+
if (this.heartbeatTimer) {
|
|
117
|
+
clearInterval(this.heartbeatTimer);
|
|
118
|
+
this.heartbeatTimer = null;
|
|
119
|
+
}
|
|
120
|
+
this.mic?.stop();
|
|
121
|
+
this.mic = null;
|
|
122
|
+
const socket = this.socket;
|
|
123
|
+
this.socket = null;
|
|
124
|
+
if (socket) {
|
|
125
|
+
await new Promise(res => socket.close(() => res()));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// ── setup ──────────────────────────────────────────────────────────────
|
|
129
|
+
async resolveAddress() {
|
|
130
|
+
if (this.cfg.host) {
|
|
131
|
+
return { host: this.cfg.host, port: this.cfg.port };
|
|
132
|
+
}
|
|
133
|
+
if (this.cfg.mqttBroker) {
|
|
134
|
+
return (0, mqtt_1.discoverServer)(this.cfg, this.log);
|
|
135
|
+
}
|
|
136
|
+
throw new Error('No adapter address: set "host" (fixed) or "mqttBroker" (discovery) in the config.');
|
|
137
|
+
}
|
|
138
|
+
openSocket() {
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
const socket = dgram.createSocket('udp4');
|
|
141
|
+
this.socket = socket;
|
|
142
|
+
socket.on('message', d => this.onMessage(d));
|
|
143
|
+
socket.on('error', e => this.log.error(`UDP error: ${e.message}`));
|
|
144
|
+
socket.once('error', reject);
|
|
145
|
+
socket.bind(this.cfg.listenPort, () => {
|
|
146
|
+
this.log.info(`Listening for TTS on UDP ${this.cfg.listenPort}.`);
|
|
147
|
+
resolve();
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/** Register, retrying with exponential backoff until the adapter acknowledges (or we stop). */
|
|
152
|
+
async registerWithRetry() {
|
|
153
|
+
let backoff = 1000;
|
|
154
|
+
while (this.running) {
|
|
155
|
+
try {
|
|
156
|
+
await this.register();
|
|
157
|
+
this.awaitingHeartbeatAck = false;
|
|
158
|
+
this.heartbeatMisses = 0;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
this.log.warn(`${e.message} — retrying in ${Math.round(backoff / 1000)} s …`);
|
|
163
|
+
await sleep(backoff);
|
|
164
|
+
backoff = Math.min(backoff * 2, RECONNECT_MAX_BACKOFF_MS);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Triggered when heartbeats stop being acknowledged: re-register (adapter probably restarted). */
|
|
169
|
+
async reconnect() {
|
|
170
|
+
if (this.reconnecting || !this.running) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.reconnecting = true;
|
|
174
|
+
this.heartbeatMisses = 0;
|
|
175
|
+
this.awaitingHeartbeatAck = false;
|
|
176
|
+
this.log.warn('Adapter unreachable — re-registering …');
|
|
177
|
+
try {
|
|
178
|
+
await this.registerWithRetry();
|
|
179
|
+
this.log.info('Re-registered with the adapter.');
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
this.reconnecting = false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
heartbeatTick() {
|
|
186
|
+
if (this.reconnecting) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (this.awaitingHeartbeatAck) {
|
|
190
|
+
this.heartbeatMisses++;
|
|
191
|
+
this.log.warn(`Heartbeat not acknowledged (${this.heartbeatMisses}/${MAX_HEARTBEAT_MISSES}).`);
|
|
192
|
+
if (this.heartbeatMisses >= MAX_HEARTBEAT_MISSES) {
|
|
193
|
+
void this.reconnect();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
this.awaitingHeartbeatAck = true;
|
|
198
|
+
this.sendControl({ type: 'heartbeat', device: this.cfg.device });
|
|
199
|
+
}
|
|
200
|
+
register() {
|
|
201
|
+
return new Promise((resolve, reject) => {
|
|
202
|
+
this.registerResolve = resolve;
|
|
203
|
+
this.sendControl({
|
|
204
|
+
type: 'register',
|
|
205
|
+
device: this.cfg.device,
|
|
206
|
+
room: this.cfg.room,
|
|
207
|
+
listen_port: this.cfg.listenPort,
|
|
208
|
+
});
|
|
209
|
+
this.log.info(`Registration sent to ${this.serverHost}:${this.serverPort} (device '${this.cfg.device}').`);
|
|
210
|
+
setTimeout(() => {
|
|
211
|
+
if (this.registerResolve) {
|
|
212
|
+
this.registerResolve = null;
|
|
213
|
+
reject(new Error(`registration timed out after ${this.cfg.registrationTimeoutMs} ms`));
|
|
214
|
+
}
|
|
215
|
+
}, this.cfg.registrationTimeoutMs);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// ── microphone → wake word → recording ─────────────────────────────────
|
|
219
|
+
onMicData(chunk) {
|
|
220
|
+
if (!this.micBytesLogged) {
|
|
221
|
+
this.micBytesLogged = true;
|
|
222
|
+
this.log.info(`Microphone is producing audio (first chunk ${chunk.length} bytes).`);
|
|
223
|
+
}
|
|
224
|
+
this.micRemainder = Buffer.concat([this.micRemainder, chunk]);
|
|
225
|
+
if (!this.pumping) {
|
|
226
|
+
void this.pump();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async pump() {
|
|
230
|
+
this.pumping = true;
|
|
231
|
+
try {
|
|
232
|
+
while (this.micRemainder.length >= FRAME_BYTES) {
|
|
233
|
+
const frame = Buffer.from(this.micRemainder.subarray(0, FRAME_BYTES));
|
|
234
|
+
this.micRemainder = this.micRemainder.subarray(FRAME_BYTES);
|
|
235
|
+
await this.handleFrame(frame);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
this.log.error(`frame processing error: ${e.message}`);
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
this.pumping = false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async handleFrame(frame) {
|
|
246
|
+
if (this.recording) {
|
|
247
|
+
this.sendAudio(frame);
|
|
248
|
+
this.recFrames++;
|
|
249
|
+
const level = (0, vad_1.rms)(frame);
|
|
250
|
+
if (level > this.recPeakRms) {
|
|
251
|
+
this.recPeakRms = level;
|
|
252
|
+
}
|
|
253
|
+
if (this.silence.push(frame)) {
|
|
254
|
+
this.endRecording();
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
// Wake-word listening: keep a rolling pre-buffer so speech during inference is not lost.
|
|
259
|
+
this.preBuffer.push(frame);
|
|
260
|
+
if (this.preBuffer.length > this.cfg.preBufferChunks) {
|
|
261
|
+
this.preBuffer.shift();
|
|
262
|
+
}
|
|
263
|
+
const score = await this.wakeword.process(frame);
|
|
264
|
+
// Periodic diagnostics: every ~2 s report how many frames ran and the peak score, so you can
|
|
265
|
+
// watch the score rise while saying the wake word and tune `wakewordThreshold`.
|
|
266
|
+
this.wakeFrames++;
|
|
267
|
+
if (score !== null && score > this.wakeMaxScore) {
|
|
268
|
+
this.wakeMaxScore = score;
|
|
269
|
+
}
|
|
270
|
+
const level = (0, vad_1.rms)(frame);
|
|
271
|
+
if (level > this.wakeMaxRms) {
|
|
272
|
+
this.wakeMaxRms = level;
|
|
273
|
+
}
|
|
274
|
+
if (this.wakeFrames >= 25) {
|
|
275
|
+
this.log.debug(`wake: ${this.wakeFrames} frames, peak mic RMS ${this.wakeMaxRms.toFixed(0)}, ` +
|
|
276
|
+
`peak score ${this.wakeMaxScore.toFixed(3)} (threshold ${this.cfg.wakewordThreshold})`);
|
|
277
|
+
this.wakeFrames = 0;
|
|
278
|
+
this.wakeMaxScore = 0;
|
|
279
|
+
this.wakeMaxRms = 0;
|
|
280
|
+
}
|
|
281
|
+
if (this.wakeword.triggered(score)) {
|
|
282
|
+
this.log.info(`Wake word detected (score ${score.toFixed(3)}).`);
|
|
283
|
+
this.wakeword.reset();
|
|
284
|
+
await this.startRecording();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async startRecording() {
|
|
288
|
+
this.setStatus('listening');
|
|
289
|
+
this.ttsDiscard = true; // drop any late TTS from a previous turn
|
|
290
|
+
await (0, audio_1.playPcm)(this.plingPcm, protocol_1.AUDIO_SAMPLE_RATE, this.backend, this.cfg.speakerDevice, this.log);
|
|
291
|
+
this.ttsDiscard = false;
|
|
292
|
+
this.ttsChunks = [];
|
|
293
|
+
// Drop everything captured up to now (the beep echo + any inference backlog) so recording runs
|
|
294
|
+
// in real time — otherwise the silence detector races through buffered quiet frames and ends the
|
|
295
|
+
// utterance before you finish speaking (→ the adapter's STT gets silence → "(empty)").
|
|
296
|
+
this.micRemainder = Buffer.alloc(0);
|
|
297
|
+
this.preBuffer = [];
|
|
298
|
+
this.recFrames = 0;
|
|
299
|
+
this.recPeakRms = 0;
|
|
300
|
+
this.silence = new vad_1.SilenceDetector(this.cfg.silenceThreshold, Math.round(this.cfg.silenceMs / FRAME_MS), Math.round(this.cfg.minRecordMs / FRAME_MS), Math.round(this.cfg.maxRecordMs / FRAME_MS));
|
|
301
|
+
this.recording = true;
|
|
302
|
+
this.log.info('Beep done — listening for your command …');
|
|
303
|
+
}
|
|
304
|
+
endRecording() {
|
|
305
|
+
this.recording = false;
|
|
306
|
+
this.silence = null;
|
|
307
|
+
this.sendControl({ type: 'audio_end', device: this.cfg.device });
|
|
308
|
+
const maxFrames = Math.round(this.cfg.maxRecordMs / FRAME_MS);
|
|
309
|
+
const reason = this.recFrames >= maxFrames ? 'max length' : 'silence';
|
|
310
|
+
this.log.info(`Recording finished: ${this.recFrames} frames (${this.recFrames * FRAME_MS} ms), ` +
|
|
311
|
+
`peak RMS ${this.recPeakRms.toFixed(0)}, ended on ${reason} — audio_end sent.`);
|
|
312
|
+
}
|
|
313
|
+
// ── incoming UDP (control + TTS) ───────────────────────────────────────
|
|
314
|
+
onMessage(data) {
|
|
315
|
+
if (!data.length) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const { type, payload } = (0, protocol_1.decodePacket)(data);
|
|
319
|
+
if (type === protocol_1.TYPE_AUDIO) {
|
|
320
|
+
return; // satellites do not receive audio
|
|
321
|
+
}
|
|
322
|
+
if (type === protocol_1.TYPE_TTS) {
|
|
323
|
+
if (!this.ttsDiscard) {
|
|
324
|
+
this.ttsChunks.push(Buffer.from(payload));
|
|
325
|
+
}
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (type !== protocol_1.TYPE_CONTROL) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
let msg;
|
|
332
|
+
try {
|
|
333
|
+
msg = JSON.parse(payload.toString('utf8'));
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
switch (msg.type) {
|
|
339
|
+
case 'registered':
|
|
340
|
+
if (msg.ok && this.registerResolve) {
|
|
341
|
+
this.log.info('Registration confirmed (ACK).');
|
|
342
|
+
this.registerResolve();
|
|
343
|
+
this.registerResolve = null;
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
case 'reregister':
|
|
347
|
+
this.register().catch(e => this.log.warn(`re-register failed: ${e.message}`));
|
|
348
|
+
break;
|
|
349
|
+
case 'status':
|
|
350
|
+
if (msg.state) {
|
|
351
|
+
this.setStatus(msg.state);
|
|
352
|
+
}
|
|
353
|
+
break;
|
|
354
|
+
case 'tts_end':
|
|
355
|
+
void this.playTts(msg.sample_rate || protocol_1.AUDIO_SAMPLE_RATE);
|
|
356
|
+
break;
|
|
357
|
+
case 'heartbeat_ack':
|
|
358
|
+
this.awaitingHeartbeatAck = false;
|
|
359
|
+
this.heartbeatMisses = 0;
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async playTts(sampleRate) {
|
|
364
|
+
const pcm = Buffer.concat(this.ttsChunks);
|
|
365
|
+
this.ttsChunks = [];
|
|
366
|
+
if (!pcm.length) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
this.log.info(`Playing reply (${(pcm.length / 2 / sampleRate).toFixed(1)} s @ ${sampleRate} Hz).`);
|
|
370
|
+
await (0, audio_1.playPcm)(pcm, sampleRate, this.backend, this.cfg.speakerDevice, this.log);
|
|
371
|
+
}
|
|
372
|
+
// ── senders / status ───────────────────────────────────────────────────
|
|
373
|
+
sendAudio(pcm) {
|
|
374
|
+
this.socket?.send((0, protocol_1.encodeAudio)(pcm), this.serverPort, this.serverHost);
|
|
375
|
+
}
|
|
376
|
+
sendControl(msg) {
|
|
377
|
+
this.socket?.send((0, protocol_1.encodeControl)(msg), this.serverPort, this.serverHost);
|
|
378
|
+
}
|
|
379
|
+
setStatus(state) {
|
|
380
|
+
this.host.onStatus?.(state);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
exports.Satellite = Satellite;
|
|
384
|
+
//# sourceMappingURL=satellite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"satellite.js","sourceRoot":"","sources":["../src/satellite.ts"],"names":[],"mappings":"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"}
|
package/build/service.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.installService = installService;
|
|
37
|
+
exports.uninstallService = uninstallService;
|
|
38
|
+
/**
|
|
39
|
+
* Self-install as a systemd service: `assistant-satellite install [config.json]`.
|
|
40
|
+
* Generates /etc/systemd/system/assistant-satellite.service pointing at this node binary + cli.js +
|
|
41
|
+
* the given config, then daemon-reload + enable --now. Needs root (re-run with sudo).
|
|
42
|
+
*/
|
|
43
|
+
const fs = __importStar(require("node:fs"));
|
|
44
|
+
const os = __importStar(require("node:os"));
|
|
45
|
+
const path = __importStar(require("node:path"));
|
|
46
|
+
const node_child_process_1 = require("node:child_process");
|
|
47
|
+
const config_1 = require("./config");
|
|
48
|
+
const audio_1 = require("./audio");
|
|
49
|
+
const checks_1 = require("./checks");
|
|
50
|
+
const SERVICE_NAME = 'assistant-satellite';
|
|
51
|
+
const SERVICE_PATH = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
52
|
+
function run(cmd, args, log) {
|
|
53
|
+
log.debug(`$ ${cmd} ${args.join(' ')}`);
|
|
54
|
+
(0, node_child_process_1.execFileSync)(cmd, args, { stdio: 'inherit' });
|
|
55
|
+
}
|
|
56
|
+
function requireLinux() {
|
|
57
|
+
if (process.platform !== 'linux') {
|
|
58
|
+
throw new Error('Service install/uninstall is only supported on Linux (systemd).');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function installService(configPath, log, force = false) {
|
|
62
|
+
requireLinux();
|
|
63
|
+
const absConfig = path.resolve(configPath);
|
|
64
|
+
if (!fs.existsSync(absConfig)) {
|
|
65
|
+
throw new Error(`config not found: ${absConfig} — run once to create/edit it, then install.`);
|
|
66
|
+
}
|
|
67
|
+
// Preflight: verify rights, audio in/out, tools, config before writing the unit.
|
|
68
|
+
let cfg;
|
|
69
|
+
try {
|
|
70
|
+
cfg = (0, config_1.loadConfig)(JSON.parse(fs.readFileSync(absConfig, 'utf8')));
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
throw new Error(`cannot read ${absConfig}: ${e.message}`);
|
|
74
|
+
}
|
|
75
|
+
log.info('Preflight checks:');
|
|
76
|
+
const passed = (0, checks_1.printChecks)((0, checks_1.runChecks)(cfg, (0, audio_1.resolveBackend)(cfg.audioBackend), true), log);
|
|
77
|
+
if (!passed && !force) {
|
|
78
|
+
throw new Error('preflight checks failed — fix the above, or re-run with --force to install anyway.');
|
|
79
|
+
}
|
|
80
|
+
const nodeBin = process.execPath;
|
|
81
|
+
const cliPath = path.join(__dirname, 'cli.js');
|
|
82
|
+
const workingDir = path.dirname(absConfig);
|
|
83
|
+
// Run the service as the invoking user (sudo → the real user), not root, so audio access is normal.
|
|
84
|
+
const user = process.env.SUDO_USER || os.userInfo().username;
|
|
85
|
+
const unit = `[Unit]\n` +
|
|
86
|
+
`Description=ioBroker assistant satellite\n` +
|
|
87
|
+
`After=network-online.target sound.target\n` +
|
|
88
|
+
`Wants=network-online.target\n\n` +
|
|
89
|
+
`[Service]\n` +
|
|
90
|
+
`Type=simple\n` +
|
|
91
|
+
`WorkingDirectory=${workingDir}\n` +
|
|
92
|
+
`ExecStart=${nodeBin} ${cliPath} ${absConfig}\n` +
|
|
93
|
+
`Restart=always\n` +
|
|
94
|
+
`RestartSec=5\n` +
|
|
95
|
+
`User=${user}\n` +
|
|
96
|
+
`SupplementaryGroups=audio\n\n` +
|
|
97
|
+
`[Install]\n` +
|
|
98
|
+
`WantedBy=multi-user.target\n`;
|
|
99
|
+
try {
|
|
100
|
+
fs.writeFileSync(SERVICE_PATH, unit);
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
if (e.code === 'EACCES') {
|
|
104
|
+
throw new Error(`cannot write ${SERVICE_PATH} — re-run with sudo.`);
|
|
105
|
+
}
|
|
106
|
+
throw e;
|
|
107
|
+
}
|
|
108
|
+
log.info(`Wrote ${SERVICE_PATH} (runs as User=${user}).`);
|
|
109
|
+
run('systemctl', ['daemon-reload'], log);
|
|
110
|
+
run('systemctl', ['enable', '--now', SERVICE_NAME], log);
|
|
111
|
+
log.info(`Service '${SERVICE_NAME}' installed and started.`);
|
|
112
|
+
log.info(` logs: journalctl -u ${SERVICE_NAME} -f`);
|
|
113
|
+
log.info(` restart: sudo systemctl restart ${SERVICE_NAME}`);
|
|
114
|
+
log.info(` remove: sudo ${nodeBin} ${cliPath} uninstall`);
|
|
115
|
+
}
|
|
116
|
+
function uninstallService(log) {
|
|
117
|
+
requireLinux();
|
|
118
|
+
try {
|
|
119
|
+
run('systemctl', ['disable', '--now', SERVICE_NAME], log);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
/* not enabled/running — ignore */
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
fs.unlinkSync(SERVICE_PATH);
|
|
126
|
+
log.info(`Removed ${SERVICE_PATH}.`);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
const code = e.code;
|
|
130
|
+
if (code === 'EACCES') {
|
|
131
|
+
throw new Error(`cannot remove ${SERVICE_PATH} — re-run with sudo.`);
|
|
132
|
+
}
|
|
133
|
+
if (code !== 'ENOENT') {
|
|
134
|
+
throw e;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
run('systemctl', ['daemon-reload'], log);
|
|
138
|
+
log.info(`Service '${SERVICE_NAME}' removed.`);
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/build/vad.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Simple RMS-energy voice-activity detection (ported from the Hannah satellite). */
|
|
2
|
+
/** RMS energy (0..32768) of a 16-bit signed LE PCM frame. */
|
|
3
|
+
export declare function rms(pcm: Buffer): number;
|
|
4
|
+
/**
|
|
5
|
+
* Tracks trailing silence across frames to decide when an utterance ends.
|
|
6
|
+
* Silence detection only kicks in after `minFrames` (so a short pause at the very start
|
|
7
|
+
* does not cut speech off).
|
|
8
|
+
*/
|
|
9
|
+
export declare class SilenceDetector {
|
|
10
|
+
private readonly threshold;
|
|
11
|
+
private readonly silenceFrames;
|
|
12
|
+
private readonly minFrames;
|
|
13
|
+
private readonly maxFrames;
|
|
14
|
+
private silentFrames;
|
|
15
|
+
private recordedFrames;
|
|
16
|
+
constructor(threshold: number, silenceFrames: number, minFrames: number, maxFrames: number);
|
|
17
|
+
/** Feed one frame; returns true when recording should stop (silence reached or max length). */
|
|
18
|
+
push(pcm: Buffer): boolean;
|
|
19
|
+
}
|
package/build/vad.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Simple RMS-energy voice-activity detection (ported from the Hannah satellite). */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SilenceDetector = void 0;
|
|
5
|
+
exports.rms = rms;
|
|
6
|
+
/** RMS energy (0..32768) of a 16-bit signed LE PCM frame. */
|
|
7
|
+
function rms(pcm) {
|
|
8
|
+
const n = Math.floor(pcm.length / 2);
|
|
9
|
+
if (!n) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
let sum = 0;
|
|
13
|
+
for (let i = 0; i < n; i++) {
|
|
14
|
+
const s = pcm.readInt16LE(i * 2);
|
|
15
|
+
sum += s * s;
|
|
16
|
+
}
|
|
17
|
+
return Math.sqrt(sum / n);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Tracks trailing silence across frames to decide when an utterance ends.
|
|
21
|
+
* Silence detection only kicks in after `minFrames` (so a short pause at the very start
|
|
22
|
+
* does not cut speech off).
|
|
23
|
+
*/
|
|
24
|
+
class SilenceDetector {
|
|
25
|
+
threshold;
|
|
26
|
+
silenceFrames;
|
|
27
|
+
minFrames;
|
|
28
|
+
maxFrames;
|
|
29
|
+
silentFrames = 0;
|
|
30
|
+
recordedFrames = 0;
|
|
31
|
+
constructor(threshold, silenceFrames, minFrames, maxFrames) {
|
|
32
|
+
this.threshold = threshold;
|
|
33
|
+
this.silenceFrames = silenceFrames;
|
|
34
|
+
this.minFrames = minFrames;
|
|
35
|
+
this.maxFrames = maxFrames;
|
|
36
|
+
}
|
|
37
|
+
/** Feed one frame; returns true when recording should stop (silence reached or max length). */
|
|
38
|
+
push(pcm) {
|
|
39
|
+
this.recordedFrames++;
|
|
40
|
+
if (this.recordedFrames >= this.maxFrames) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (this.recordedFrames < this.minFrames) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (rms(pcm) < this.threshold) {
|
|
47
|
+
this.silentFrames++;
|
|
48
|
+
return this.silentFrames >= this.silenceFrames;
|
|
49
|
+
}
|
|
50
|
+
this.silentFrames = 0;
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.SilenceDetector = SilenceDetector;
|
|
55
|
+
//# sourceMappingURL=vad.js.map
|
package/build/vad.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vad.js","sourceRoot":"","sources":["../src/vad.ts"],"names":[],"mappings":";AAAA,qFAAqF;;;AAGrF,kBAWC;AAZD,6DAA6D;AAC7D,SAAgB,GAAG,CAAC,GAAW;IAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,CAAC,CAAC;IACb,CAAC;IACD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACjC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAa,eAAe;IAKH;IACA;IACA;IACA;IAPb,YAAY,GAAG,CAAC,CAAC;IACjB,cAAc,GAAG,CAAC,CAAC;IAE3B,YACqB,SAAiB,EACjB,aAAqB,EACrB,SAAiB,EACjB,SAAiB;QAHjB,cAAS,GAAT,SAAS,CAAQ;QACjB,kBAAa,GAAb,aAAa,CAAQ;QACrB,cAAS,GAAT,SAAS,CAAQ;QACjB,cAAS,GAAT,SAAS,CAAQ;IACnC,CAAC;IAEJ,+FAA+F;IAC/F,IAAI,CAAC,GAAW;QACZ,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACxC,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,aAAa,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,OAAO,KAAK,CAAC;IACjB,CAAC;CACJ;AA3BD,0CA2BC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ModelPaths } from './models';
|
|
2
|
+
import type { Logger } from './index';
|
|
3
|
+
export declare class WakeWord {
|
|
4
|
+
private readonly models;
|
|
5
|
+
private readonly threshold;
|
|
6
|
+
private readonly log;
|
|
7
|
+
private melspec;
|
|
8
|
+
private embedding;
|
|
9
|
+
private classifier;
|
|
10
|
+
private melBuffer;
|
|
11
|
+
private melProcessed;
|
|
12
|
+
private embBuffer;
|
|
13
|
+
private melShapeLogged;
|
|
14
|
+
private embShapeLogged;
|
|
15
|
+
constructor(models: ModelPaths, threshold: number, log: Logger);
|
|
16
|
+
load(): Promise<void>;
|
|
17
|
+
/** Reset the streaming buffers (call after a detection so it re-arms cleanly). */
|
|
18
|
+
reset(): void;
|
|
19
|
+
/**
|
|
20
|
+
* Feed one audio chunk (16-bit signed LE PCM, ideally ~1280 samples = 80 ms) and return the
|
|
21
|
+
* current wake-word score, or null if not enough context yet.
|
|
22
|
+
*/
|
|
23
|
+
process(pcm: Buffer): Promise<number | null>;
|
|
24
|
+
/** True when the given score crosses the configured detection threshold. */
|
|
25
|
+
triggered(score: number | null): boolean;
|
|
26
|
+
private appendMel;
|
|
27
|
+
private appendEmbeddings;
|
|
28
|
+
private classify;
|
|
29
|
+
}
|