fixed tts format, added trigger words settings
This commit is contained in:
+20
-5
@@ -72,7 +72,7 @@ BLOCK_SIZE = 1280 # 80ms bei 16kHz — gut fuer Wake-Word-Erkennung
|
|||||||
RECORD_SECONDS = 8 # Max. Aufnahmedauer nach Wake-Word
|
RECORD_SECONDS = 8 # Max. Aufnahmedauer nach Wake-Word
|
||||||
|
|
||||||
# Epische Trigger — bei diesen Woertern spricht Thorsten
|
# Epische Trigger — bei diesen Woertern spricht Thorsten
|
||||||
EPIC_TRIGGERS = [
|
EPIC_TRIGGERS_DEFAULT = [
|
||||||
"deploy",
|
"deploy",
|
||||||
"erfolgreich",
|
"erfolgreich",
|
||||||
"alarm",
|
"alarm",
|
||||||
@@ -84,6 +84,24 @@ EPIC_TRIGGERS = [
|
|||||||
"aufgabe abgeschlossen",
|
"aufgabe abgeschlossen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Trigger aus Shared-Config laden (von Diagnostic gespeichert)
|
||||||
|
TRIGGERS_FILE = "/shared/config/highlight_triggers.json"
|
||||||
|
|
||||||
|
def load_epic_triggers():
|
||||||
|
"""Laedt Highlight-Trigger aus Shared-Config oder nutzt Defaults."""
|
||||||
|
try:
|
||||||
|
if os.path.exists(TRIGGERS_FILE):
|
||||||
|
with open(TRIGGERS_FILE) as f:
|
||||||
|
triggers = json.load(f)
|
||||||
|
if isinstance(triggers, list) and len(triggers) > 0:
|
||||||
|
logger.info("Highlight-Trigger geladen: %d aus %s", len(triggers), TRIGGERS_FILE)
|
||||||
|
return triggers
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("Highlight-Trigger laden fehlgeschlagen: %s — nutze Defaults", e)
|
||||||
|
return EPIC_TRIGGERS_DEFAULT
|
||||||
|
|
||||||
|
EPIC_TRIGGERS = load_epic_triggers()
|
||||||
|
|
||||||
|
|
||||||
def load_config() -> dict[str, str]:
|
def load_config() -> dict[str, str]:
|
||||||
"""Laedt Konfiguration aus /config/aria.env."""
|
"""Laedt Konfiguration aus /config/aria.env."""
|
||||||
@@ -184,10 +202,7 @@ class VoiceEngine:
|
|||||||
tmp_path = tmp.name
|
tmp_path = tmp.name
|
||||||
|
|
||||||
with wave.open(tmp_path, "wb") as wav_file:
|
with wave.open(tmp_path, "wb") as wav_file:
|
||||||
wav_file.setnchannels(1)
|
voice.synthesize_wav(text, wav_file)
|
||||||
wav_file.setsampwidth(2) # 16-bit
|
|
||||||
wav_file.setframerate(voice.config.sample_rate)
|
|
||||||
voice.synthesize(text, wav_file)
|
|
||||||
|
|
||||||
audio_data = Path(tmp_path).read_bytes()
|
audio_data = Path(tmp_path).read_bytes()
|
||||||
Path(tmp_path).unlink(missing_ok=True)
|
Path(tmp_path).unlink(missing_ok=True)
|
||||||
|
|||||||
@@ -396,6 +396,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Highlight-Trigger -->
|
||||||
|
<div class="settings-section">
|
||||||
|
<h2>Highlight-Trigger</h2>
|
||||||
|
<div style="font-size:11px;color:#8888AA;margin-bottom:8px;">
|
||||||
|
Woerter die automatisch die Highlight-Stimme (Thorsten) ausloesen.
|
||||||
|
Eines pro Zeile. Aenderungen werden in der Bridge gespeichert.
|
||||||
|
</div>
|
||||||
|
<div class="card" style="max-width:500px;">
|
||||||
|
<textarea id="highlight-triggers" rows="8" style="width:100%;box-sizing:border-box;background:#1E1E2E;border:1px solid #2A2A3E;border-radius:6px;padding:8px;color:#fff;font-size:13px;font-family:monospace;resize:vertical;"
|
||||||
|
placeholder="Lade..."></textarea>
|
||||||
|
<div style="display:flex;gap:8px;margin-top:8px;">
|
||||||
|
<button class="btn" onclick="saveHighlightTriggers()" style="flex:1;">Speichern</button>
|
||||||
|
<button class="btn secondary" onclick="loadHighlightTriggers()" style="flex:1;">Neu laden</button>
|
||||||
|
</div>
|
||||||
|
<div id="trigger-status" style="font-size:11px;color:#555570;margin-top:6px;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Tool-Berechtigungen -->
|
<!-- Tool-Berechtigungen -->
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
<h2>Tool-Berechtigungen</h2>
|
<h2>Tool-Berechtigungen</h2>
|
||||||
@@ -599,6 +617,14 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type === 'trigger_list') {
|
||||||
|
const textarea = document.getElementById('highlight-triggers');
|
||||||
|
textarea.value = (msg.triggers || []).join('\n');
|
||||||
|
document.getElementById('trigger-status').textContent = msg.triggers.length + ' Trigger geladen';
|
||||||
|
document.getElementById('trigger-status').style.color = '#8888AA';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type === 'watchdog') {
|
if (msg.type === 'watchdog') {
|
||||||
const colors = { warning: '#FFD60A', fixing: '#FF9500', fixed: '#34C759', error: '#FF3B30' };
|
const colors = { warning: '#FFD60A', fixing: '#FF9500', fixed: '#34C759', error: '#FF3B30' };
|
||||||
const color = colors[msg.status] || '#FFD60A';
|
const color = colors[msg.status] || '#FFD60A';
|
||||||
@@ -1080,6 +1106,20 @@
|
|||||||
}, 120000);
|
}, 120000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Highlight-Trigger ────────────────────────
|
||||||
|
function loadHighlightTriggers() {
|
||||||
|
send({ action: 'get_triggers' });
|
||||||
|
}
|
||||||
|
function saveHighlightTriggers() {
|
||||||
|
const text = document.getElementById('highlight-triggers').value;
|
||||||
|
const triggers = text.split('\n').map(t => t.trim()).filter(t => t.length > 0);
|
||||||
|
send({ action: 'save_triggers', triggers });
|
||||||
|
document.getElementById('trigger-status').textContent = 'Gespeichert (' + triggers.length + ' Trigger)';
|
||||||
|
document.getElementById('trigger-status').style.color = '#34C759';
|
||||||
|
}
|
||||||
|
// Beim Tab-Wechsel zu Einstellungen: Trigger laden
|
||||||
|
const origSwitchMainTab = typeof switchMainTab === 'function' ? switchMainTab : null;
|
||||||
|
|
||||||
// ── Modus-Wechsel ────────────────────────────
|
// ── Modus-Wechsel ────────────────────────────
|
||||||
let currentMode = 'normal';
|
let currentMode = 'normal';
|
||||||
const MODE_LABELS = { normal: 'Normal', dnd: 'Nicht stoeren', whisper: 'Fluestern', hangar: 'Hangar', gaming: 'Gaming' };
|
const MODE_LABELS = { normal: 'Normal', dnd: 'Nicht stoeren', whisper: 'Fluestern', hangar: 'Hangar', gaming: 'Gaming' };
|
||||||
@@ -1514,6 +1554,8 @@
|
|||||||
document.querySelectorAll('.main-nav-btn').forEach(b => {
|
document.querySelectorAll('.main-nav-btn').forEach(b => {
|
||||||
if (b.textContent.trim().toLowerCase().includes(tab === 'main' ? 'main' : 'einstellung')) b.classList.add('active');
|
if (b.textContent.trim().toLowerCase().includes(tab === 'main' ? 'main' : 'einstellung')) b.classList.add('active');
|
||||||
});
|
});
|
||||||
|
// Einstellungen: Trigger laden
|
||||||
|
if (tab === 'settings') loadHighlightTriggers();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Einstellungen: Tool-Berechtigungen ──────────────────
|
// ── Einstellungen: Tool-Berechtigungen ──────────────────
|
||||||
|
|||||||
@@ -1147,6 +1147,10 @@ wss.on("connection", (ws) => {
|
|||||||
if (ws._sshSock) ws._sshSock.write(msg.data);
|
if (ws._sshSock) ws._sshSock.write(msg.data);
|
||||||
} else if (msg.action === "live_ssh_close") {
|
} else if (msg.action === "live_ssh_close") {
|
||||||
if (ws._sshSock) { ws._sshSock.end(); ws._sshSock = null; }
|
if (ws._sshSock) { ws._sshSock.end(); ws._sshSock = null; }
|
||||||
|
} else if (msg.action === "get_triggers") {
|
||||||
|
handleGetTriggers(ws);
|
||||||
|
} else if (msg.action === "save_triggers") {
|
||||||
|
handleSaveTriggers(ws, msg.triggers || []);
|
||||||
} else if (msg.action === "test_tts") {
|
} else if (msg.action === "test_tts") {
|
||||||
handleTestTTS(ws, msg.voice || "ramona", msg.text || "Test");
|
handleTestTTS(ws, msg.voice || "ramona", msg.text || "Test");
|
||||||
} else if (msg.action === "check_tts") {
|
} else if (msg.action === "check_tts") {
|
||||||
@@ -1277,6 +1281,44 @@ function startLiveSSH(clientWs) {
|
|||||||
createReq.end(createBody);
|
createReq.end(createBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Highlight-Trigger ─────────────────────────────────
|
||||||
|
|
||||||
|
const TRIGGERS_FILE = "/shared/config/highlight_triggers.json";
|
||||||
|
|
||||||
|
async function handleGetTriggers(clientWs) {
|
||||||
|
try {
|
||||||
|
// Zuerst aus Shared Volume lesen, dann Fallback auf Bridge-Defaults
|
||||||
|
let triggers;
|
||||||
|
if (fs.existsSync(TRIGGERS_FILE)) {
|
||||||
|
triggers = JSON.parse(fs.readFileSync(TRIGGERS_FILE, "utf-8"));
|
||||||
|
} else {
|
||||||
|
// Defaults aus der Bridge lesen
|
||||||
|
const result = await dockerExec("aria-bridge", `python3 -c "
|
||||||
|
import sys; sys.path.insert(0,'/app')
|
||||||
|
from aria_bridge import EPIC_TRIGGERS
|
||||||
|
print('\\n'.join(EPIC_TRIGGERS))
|
||||||
|
"`);
|
||||||
|
triggers = result.trim().split("\n").filter(t => t);
|
||||||
|
}
|
||||||
|
clientWs.send(JSON.stringify({ type: "trigger_list", triggers }));
|
||||||
|
} catch (err) {
|
||||||
|
clientWs.send(JSON.stringify({ type: "trigger_list", triggers: [], error: err.message }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSaveTriggers(clientWs, triggers) {
|
||||||
|
try {
|
||||||
|
// In Shared Volume speichern (fuer Bridge lesbar)
|
||||||
|
fs.mkdirSync("/shared/config", { recursive: true });
|
||||||
|
fs.writeFileSync(TRIGGERS_FILE, JSON.stringify(triggers, null, 2));
|
||||||
|
log("info", "server", `${triggers.length} Highlight-Trigger gespeichert`);
|
||||||
|
// Bridge informieren (wird beim naechsten Start geladen)
|
||||||
|
clientWs.send(JSON.stringify({ type: "trigger_list", triggers }));
|
||||||
|
} catch (err) {
|
||||||
|
log("error", "server", `Trigger speichern fehlgeschlagen: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── TTS Diagnose ──────────────────────────────────────
|
// ── TTS Diagnose ──────────────────────────────────────
|
||||||
async function handleTestTTS(clientWs, voice, text) {
|
async function handleTestTTS(clientWs, voice, text) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user