node-red-contrib-knx-ultimate 6.4.1 → 7.0.0

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +124 -121
  2. package/README.md +4 -0
  3. package/nodes/knxUltimateAI.html +9 -5
  4. package/nodes/knxUltimateAI.js +143 -143
  5. package/nodes/knxUltimateAIHomeAssistant.html +7 -5
  6. package/nodes/knxUltimateAIHomeAssistant.js +1 -1
  7. package/nodes/knxUltimateHueController.html +1 -1
  8. package/nodes/knxUltimateMatterBridge.html +2 -2
  9. package/nodes/knxUltimateMatterControllerDevice.html +12 -12
  10. package/nodes/locales/de/knxUltimateAI.html +4 -199
  11. package/nodes/locales/de/knxUltimateAI.json +6 -6
  12. package/nodes/locales/de/knxUltimateViewer.html +1 -1
  13. package/nodes/locales/en/knxUltimateAI.html +4 -211
  14. package/nodes/locales/en/knxUltimateAI.json +6 -6
  15. package/nodes/locales/en/knxUltimateViewer.html +1 -1
  16. package/nodes/locales/es/knxUltimateAI.html +4 -199
  17. package/nodes/locales/es/knxUltimateAI.json +6 -6
  18. package/nodes/locales/es/knxUltimateViewer.html +1 -1
  19. package/nodes/locales/fr/knxUltimateAI.html +4 -199
  20. package/nodes/locales/fr/knxUltimateAI.json +6 -6
  21. package/nodes/locales/fr/knxUltimateViewer.html +1 -1
  22. package/nodes/locales/it/knxUltimateAI.html +4 -211
  23. package/nodes/locales/it/knxUltimateAI.json +6 -6
  24. package/nodes/locales/it/knxUltimateViewer.html +1 -1
  25. package/nodes/locales/zh-CN/knxUltimateAI.html +4 -199
  26. package/nodes/locales/zh-CN/knxUltimateAI.json +6 -6
  27. package/nodes/locales/zh-CN/knxUltimateViewer.html +1 -1
  28. package/nodes/plugins/knxUltimateAI-vue/assets/app.css +1 -1
  29. package/nodes/plugins/knxUltimateAI-vue/assets/app.js +10 -10
  30. package/nodes/plugins/knxUltimateAI-vue/index.html +1 -1
  31. package/nodes/plugins/knxUltimateViewer-vue/assets/app.js +2 -2
  32. package/nodes/utils/knxAiChatContext.js +4 -4
  33. package/nodes/utils/knxAiHomeMemory.js +2 -2
  34. package/nodes/utils/knxAiScheduler.js +2 -2
  35. package/package.json +2 -2
  36. package/resources/KNXAIChatAdapterMappings.js +4 -4
  37. package/examples/KNX AI - Conversational Control with Confirmation.json +0 -395
  38. package/examples/KNX AI - Summary Anomalies and Ask.json +0 -226
  39. package/examples/KNX AI - Telegrambot Direct Chat.json +0 -198
@@ -537,7 +537,7 @@ const getKnxAiSetupDoctorCopy = (language) => {
537
537
  status: { ready: 'Ready', attention: 'Almost ready', blocked: 'Action needed' },
538
538
  checks: {
539
539
  gateway: ['KNX gateway', details => !details.configured ? 'Select a KNX Ultimate gateway and deploy.' : details.connected ? `Connected to ${details.name || 'the configured gateway'}.` : `${details.name || 'The configured gateway'} is not connected yet.`],
540
- ets: ['ETS project', details => details.objectCount > 0 ? `${details.objectCount} unique group addresses and ${details.areaCount} ETS areas/groups recognized.` : 'No ETS group address is available to KNX AI. Configure ETS object access and verify the ETS CSV import in the gateway.'],
540
+ ets: ['ETS project', details => details.objectCount > 0 ? `${details.objectCount} unique group addresses and ${details.areaCount} ETS areas/groups recognized.` : 'No ETS group address is available to Cerebrum Ultimate. Configure ETS object access and verify the ETS CSV import in the gateway.'],
541
541
  assistant: ['AI assistant', details => details.enabled ? 'The assistant is enabled.' : 'Enable the LLM assistant to start conversations.'],
542
542
  provider: ['Provider and model', details => details.ready ? `${details.providerLabel} · ${details.model}` : `Complete the missing provider settings: ${details.missing.join(', ')}.`],
543
543
  providerConnection: ['Provider connection', details => details.state === 'reachable' ? details.selectedModelAvailable === false ? `Provider reached, but the selected model “${details.model}” is not in its reported catalog.` : `Provider reached successfully${details.modelCount > 0 ? `; ${details.modelCount} model(s) reported` : ''}.` : details.state === 'checking' ? 'Checking the provider without sending a chat request…' : details.state === 'unreachable' ? 'The configuration is saved, but the provider model endpoint did not answer. Use Refresh models to retry.' : 'The connectivity check will run after the provider is configured.'],
@@ -562,17 +562,17 @@ const getKnxAiSetupDoctorCopy = (language) => {
562
562
  openings: 'Which doors or windows are open now? Read-only.',
563
563
  climate: 'Which temperatures and climate states can you read now?',
564
564
  anomalies: 'Any KNX anomalies needing attention? Read-only.',
565
- setup: 'What is missing from my KNX AI setup?'
565
+ setup: 'What is missing from my Cerebrum Ultimate setup?'
566
566
  },
567
567
  welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0
568
568
  ? `Hello${name ? ` ${name}` : ''}! I have already oriented myself in your ETS project without sending anything to the bus. I found ${totals.groupAddresses} unique group addresses, ${totals.etsAreas} ETS areas/groups and about ${totals.logicalFunctionsEstimate} recognizable logical functions. These are ETS signals, not a count of physical devices.${assistantEnabled ? '' : '\n\nThe AI assistant is not enabled yet; Setup Doctor shows what remains to configure.'}\n\nYou can start safely with:\n${prompts.map(item => `• ${item.text}`).join('\n')}`
569
- : `Hello${name ? ` ${name}` : ''}! I am ready to help, but no ETS group address is selected for KNX AI yet. Configure ETS object access, verify the ETS CSV import and reopen Setup Doctor.${assistantEnabled ? '' : ' Also enable the AI assistant when you want to start chatting.'}`
569
+ : `Hello${name ? ` ${name}` : ''}! I am ready to help, but no ETS group address is selected for Cerebrum Ultimate yet. Configure ETS object access, verify the ETS CSV import and reopen Setup Doctor.${assistantEnabled ? '' : ' Also enable the AI assistant when you want to start chatting.'}`
570
570
  },
571
571
  it: {
572
572
  status: { ready: 'Pronto', attention: 'Quasi pronto', blocked: 'Serve un intervento' },
573
573
  checks: {
574
574
  gateway: ['Gateway KNX', details => !details.configured ? 'Seleziona un gateway KNX Ultimate e fai Deploy.' : details.connected ? `Connesso a ${details.name || 'gateway configurato'}.` : `${details.name || 'Il gateway configurato'} non è ancora connesso.`],
575
- ets: ['Progetto ETS', details => details.objectCount > 0 ? `Riconosciuti ${details.objectCount} indirizzi di gruppo univoci e ${details.areaCount} aree/gruppi ETS.` : 'Nessun indirizzo ETS è disponibile a KNX AI. Configura Accesso agli oggetti ETS e verifica il CSV importato nel gateway.'],
575
+ ets: ['Progetto ETS', details => details.objectCount > 0 ? `Riconosciuti ${details.objectCount} indirizzi di gruppo univoci e ${details.areaCount} aree/gruppi ETS.` : 'Nessun indirizzo ETS è disponibile a Cerebrum Ultimate. Configura Accesso agli oggetti ETS e verifica il CSV importato nel gateway.'],
576
576
  assistant: ['Assistente AI', details => details.enabled ? 'L’assistente è abilitato.' : 'Abilita l’assistente LLM per iniziare le conversazioni.'],
577
577
  provider: ['Provider e modello', details => details.ready ? `${details.providerLabel} · ${details.model}` : `Completa le impostazioni mancanti: ${details.missing.join(', ')}.`],
578
578
  providerConnection: ['Connessione provider', details => details.state === 'reachable' ? details.selectedModelAvailable === false ? `Provider raggiunto, ma il modello selezionato “${details.model}” non compare nel catalogo disponibile.` : `Provider raggiunto correttamente${details.modelCount > 0 ? `; disponibili ${details.modelCount} modelli` : ''}.` : details.state === 'checking' ? 'Controllo il provider senza inviare richieste chat…' : details.state === 'unreachable' ? 'La configurazione è salvata, ma l’endpoint dei modelli non ha risposto. Usa Aggiorna modelli per riprovare.' : 'Il controllo di connettività partirà dopo aver configurato il provider.'],
@@ -597,17 +597,17 @@ const getKnxAiSetupDoctorCopy = (language) => {
597
597
  openings: 'Quali porte o finestre sono aperte? Solo lettura.',
598
598
  climate: 'Quali temperature e stati clima puoi leggere ora?',
599
599
  anomalies: 'Ci sono anomalie KNX? Non eseguire comandi.',
600
- setup: 'Cosa manca nella configurazione KNX AI?'
600
+ setup: 'Cosa manca nella configurazione Cerebrum Ultimate?'
601
601
  },
602
602
  welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0
603
603
  ? `Ciao${name ? ` ${name}` : ''}! Mi sono già orientato nel progetto ETS senza inviare nulla sul bus. Ho trovato ${totals.groupAddresses} indirizzi di gruppo univoci, ${totals.etsAreas} aree/gruppi ETS e circa ${totals.logicalFunctionsEstimate} funzioni logiche riconoscibili. Sono segnali ETS, non un conteggio dei dispositivi fisici.${assistantEnabled ? '' : '\n\nL’assistente AI non è ancora abilitato; Setup Doctor mostra cosa resta da configurare.'}\n\nPuoi iniziare in sicurezza con:\n${prompts.map(item => `• ${item.text}`).join('\n')}`
604
- : `Ciao${name ? ` ${name}` : ''}! Sono pronto ad aiutarti, ma non è ancora selezionato alcun indirizzo ETS per KNX AI. Configura Accesso agli oggetti ETS, verifica il CSV importato e poi riapri Setup Doctor.${assistantEnabled ? '' : ' Abilita anche l’assistente AI quando vorrai iniziare a chattare.'}`
604
+ : `Ciao${name ? ` ${name}` : ''}! Sono pronto ad aiutarti, ma non è ancora selezionato alcun indirizzo ETS per Cerebrum Ultimate. Configura Accesso agli oggetti ETS, verifica il CSV importato e poi riapri Setup Doctor.${assistantEnabled ? '' : ' Abilita anche l’assistente AI quando vorrai iniziare a chattare.'}`
605
605
  },
606
606
  de: {
607
607
  status: { ready: 'Bereit', attention: 'Fast bereit', blocked: 'Aktion erforderlich' },
608
608
  checks: {
609
609
  gateway: ['KNX-Gateway', details => !details.configured ? 'Wählen Sie ein KNX-Ultimate-Gateway und führen Sie Deploy aus.' : details.connected ? `Mit ${details.name || 'dem konfigurierten Gateway'} verbunden.` : `${details.name || 'Das konfigurierte Gateway'} ist noch nicht verbunden.`],
610
- ets: ['ETS-Projekt', details => details.objectCount > 0 ? `${details.objectCount} eindeutige Gruppenadressen und ${details.areaCount} ETS-Bereiche/-Gruppen erkannt.` : 'Für KNX AI ist keine ETS-Gruppenadresse verfügbar. Konfigurieren Sie den Zugriff auf ETS-Objekte und prüfen Sie den ETS-CSV-Import.'],
610
+ ets: ['ETS-Projekt', details => details.objectCount > 0 ? `${details.objectCount} eindeutige Gruppenadressen und ${details.areaCount} ETS-Bereiche/-Gruppen erkannt.` : 'Für Cerebrum Ultimate ist keine ETS-Gruppenadresse verfügbar. Konfigurieren Sie den Zugriff auf ETS-Objekte und prüfen Sie den ETS-CSV-Import.'],
611
611
  assistant: ['KI-Assistent', details => details.enabled ? 'Der Assistent ist aktiviert.' : 'Aktivieren Sie den LLM-Assistenten, um Unterhaltungen zu starten.'],
612
612
  provider: ['Provider und Modell', details => details.ready ? `${details.providerLabel} · ${details.model}` : `Vervollständigen Sie: ${details.missing.join(', ')}.`],
613
613
  providerConnection: ['Provider-Verbindung', details => details.state === 'reachable' ? details.selectedModelAvailable === false ? `Provider erreichbar, aber das ausgewählte Modell „${details.model}“ fehlt im gemeldeten Katalog.` : `Provider erfolgreich erreicht${details.modelCount > 0 ? `; ${details.modelCount} Modell(e) gemeldet` : ''}.` : details.state === 'checking' ? 'Provider-Prüfung ohne Chat-Anfrage…' : details.state === 'unreachable' ? 'Die Konfiguration ist gespeichert, aber der Modell-Endpunkt antwortete nicht. Aktualisieren Sie die Modellliste erneut.' : 'Die Verbindungsprüfung startet nach der Provider-Konfiguration.'],
@@ -621,14 +621,14 @@ const getKnxAiSetupDoctorCopy = (language) => {
621
621
  homeAssistant: ['Home Assistant', details => details.ready ? 'Bereit: Cerebrum und ha-api sind als vollständiger Hin- und Rückweg verbunden.' : details.recommendationCode === 'add_ha_api' ? 'Node-RED läuft als Home-Assistant-Add-on, aber ein API-Node (ha-api) fehlt im Flow.' : details.recommendationCode === 'add_cerebrum_bridge' ? 'ha-api ist vorhanden. Fügen Sie Cerebrum Home Assistant hinzu.' : details.recommendationCode === 'wire_round_trip' ? 'Verbinden Sie Cerebrum Home Assistant → ha-api → Cerebrum Home Assistant.' : 'Home Assistant wurde nicht erkannt; die Integration ist optional.']
622
622
  },
623
623
  summary: (status, totals, issueCount) => status === 'ready' ? `Bereit: ${totals.groupAddresses} KNX-Signale, ${totals.etsAreas} ETS-Bereiche/-Gruppen und etwa ${totals.logicalFunctionsEstimate} erkennbare logische Funktionen.` : status === 'attention' ? `Fast bereit: ${totals.groupAddresses} KNX-Signale erkannt; ${issueCount} Punkt(e) brauchen Aufmerksamkeit.` : `${totals.groupAddresses} KNX-Signale erkannt, aber ${issueCount} erforderliche Punkt(e) fehlen.`,
624
- prompts: { area: name => `Nur lesen: Was wissen Sie über „${name}“?`, inventory: 'Was erkennen Sie in meiner KNX-Anlage? Nur lesen.', lights: 'Welche Leuchten können Sie jetzt lesen? Nichts ändern.', openings: 'Welche Türen oder Fenster sind offen? Nur lesen.', climate: 'Welche Temperaturen und Klimazustände lesen Sie jetzt?', anomalies: 'Gibt es KNX-Anomalien? Keine Befehle ausführen.', setup: 'Was fehlt in meiner KNX-AI-Konfiguration?' },
625
- welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0 ? `Hallo${name ? ` ${name}` : ''}! Ich habe mich bereits im ETS-Projekt orientiert, ohne etwas auf den Bus zu senden. Gefunden: ${totals.groupAddresses} eindeutige Gruppenadressen, ${totals.etsAreas} ETS-Bereiche/-Gruppen und etwa ${totals.logicalFunctionsEstimate} erkennbare logische Funktionen. Das sind ETS-Signale, keine Anzahl physischer Geräte.${assistantEnabled ? '' : '\n\nDer KI-Assistent ist noch nicht aktiviert; Setup Doctor zeigt die fehlenden Schritte.'}\n\nSicher starten mit:\n${prompts.map(item => `• ${item.text}`).join('\n')}` : `Hallo${name ? ` ${name}` : ''}! Für KNX AI ist noch keine ETS-Gruppenadresse ausgewählt. Konfigurieren Sie den Zugriff auf ETS-Objekte, prüfen Sie den CSV-Import und öffnen Sie Setup Doctor erneut.`
624
+ prompts: { area: name => `Nur lesen: Was wissen Sie über „${name}“?`, inventory: 'Was erkennen Sie in meiner KNX-Anlage? Nur lesen.', lights: 'Welche Leuchten können Sie jetzt lesen? Nichts ändern.', openings: 'Welche Türen oder Fenster sind offen? Nur lesen.', climate: 'Welche Temperaturen und Klimazustände lesen Sie jetzt?', anomalies: 'Gibt es KNX-Anomalien? Keine Befehle ausführen.', setup: 'Was fehlt in meiner Cerebrum Ultimate-Konfiguration?' },
625
+ welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0 ? `Hallo${name ? ` ${name}` : ''}! Ich habe mich bereits im ETS-Projekt orientiert, ohne etwas auf den Bus zu senden. Gefunden: ${totals.groupAddresses} eindeutige Gruppenadressen, ${totals.etsAreas} ETS-Bereiche/-Gruppen und etwa ${totals.logicalFunctionsEstimate} erkennbare logische Funktionen. Das sind ETS-Signale, keine Anzahl physischer Geräte.${assistantEnabled ? '' : '\n\nDer KI-Assistent ist noch nicht aktiviert; Setup Doctor zeigt die fehlenden Schritte.'}\n\nSicher starten mit:\n${prompts.map(item => `• ${item.text}`).join('\n')}` : `Hallo${name ? ` ${name}` : ''}! Für Cerebrum Ultimate ist noch keine ETS-Gruppenadresse ausgewählt. Konfigurieren Sie den Zugriff auf ETS-Objekte, prüfen Sie den CSV-Import und öffnen Sie Setup Doctor erneut.`
626
626
  },
627
627
  fr: {
628
628
  status: { ready: 'Prêt', attention: 'Presque prêt', blocked: 'Action requise' },
629
629
  checks: {
630
630
  gateway: ['Passerelle KNX', details => !details.configured ? 'Sélectionnez une passerelle KNX Ultimate puis déployez.' : details.connected ? `Connecté à ${details.name || 'la passerelle configurée'}.` : `${details.name || 'La passerelle configurée'} n’est pas encore connectée.`],
631
- ets: ['Projet ETS', details => details.objectCount > 0 ? `${details.objectCount} adresses de groupe uniques et ${details.areaCount} zones/groupes ETS reconnus.` : 'Aucune adresse ETS n’est disponible pour KNX AI. Configurez l’accès aux objets ETS et vérifiez l’import CSV dans la passerelle.'],
631
+ ets: ['Projet ETS', details => details.objectCount > 0 ? `${details.objectCount} adresses de groupe uniques et ${details.areaCount} zones/groupes ETS reconnus.` : 'Aucune adresse ETS n’est disponible pour Cerebrum Ultimate. Configurez l’accès aux objets ETS et vérifiez l’import CSV dans la passerelle.'],
632
632
  assistant: ['Assistant IA', details => details.enabled ? 'L’assistant est activé.' : 'Activez l’assistant LLM pour commencer les conversations.'],
633
633
  provider: ['Fournisseur et modèle', details => details.ready ? `${details.providerLabel} · ${details.model}` : `Complétez les réglages manquants : ${details.missing.join(', ')}.`],
634
634
  providerConnection: ['Connexion au fournisseur', details => details.state === 'reachable' ? details.selectedModelAvailable === false ? `Fournisseur joignable, mais le modèle sélectionné « ${details.model} » n’apparaît pas dans son catalogue.` : `Fournisseur joint avec succès${details.modelCount > 0 ? ` ; ${details.modelCount} modèle(s) signalé(s)` : ''}.` : details.state === 'checking' ? 'Vérification du fournisseur sans requête de chat…' : details.state === 'unreachable' ? 'La configuration est enregistrée, mais le point de terminaison des modèles ne répond pas. Actualisez les modèles pour réessayer.' : 'La vérification démarrera après la configuration du fournisseur.'],
@@ -642,14 +642,14 @@ const getKnxAiSetupDoctorCopy = (language) => {
642
642
  homeAssistant: ['Home Assistant', details => details.ready ? 'Prêt : Cerebrum et ha-api sont reliés par une boucle requête/réponse complète.' : details.recommendationCode === 'add_ha_api' ? 'Node-RED fonctionne comme add-on Home Assistant, mais aucun nœud API (ha-api) n’est déployé.' : details.recommendationCode === 'add_cerebrum_bridge' ? 'ha-api est présent. Ajoutez le nœud Cerebrum Home Assistant.' : details.recommendationCode === 'wire_round_trip' ? 'Reliez Cerebrum Home Assistant → ha-api → Cerebrum Home Assistant.' : 'Home Assistant n’a pas été détecté ; cette intégration est optionnelle.']
643
643
  },
644
644
  summary: (status, totals, issueCount) => status === 'ready' ? `Prêt : ${totals.groupAddresses} signaux KNX, ${totals.etsAreas} zones/groupes ETS et environ ${totals.logicalFunctionsEstimate} fonctions logiques reconnaissables.` : status === 'attention' ? `Presque prêt : ${totals.groupAddresses} signaux KNX reconnus ; ${issueCount} point(s) demandent votre attention.` : `${totals.groupAddresses} signaux KNX reconnus, mais ${issueCount} point(s) requis restent à compléter.`,
645
- prompts: { area: name => `Lecture seule : que savez-vous de « ${name} » ?`, inventory: 'Qu’avez-vous reconnu dans mon installation KNX ? Lecture seule.', lights: 'Quelles lumières pouvez-vous lire ? Ne changez rien.', openings: 'Quelles portes ou fenêtres sont ouvertes ? Lecture seule.', climate: 'Quels états de température et de climat pouvez-vous lire ?', anomalies: 'Des anomalies KNX demandent-elles attention ? Lecture seule.', setup: 'Que manque-t-il à ma configuration KNX AI ?' },
646
- welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0 ? `Bonjour${name ? ` ${name}` : ''} ! Je me suis déjà orienté dans le projet ETS sans rien envoyer sur le bus. J’ai trouvé ${totals.groupAddresses} adresses de groupe uniques, ${totals.etsAreas} zones/groupes ETS et environ ${totals.logicalFunctionsEstimate} fonctions logiques reconnaissables. Ce sont des signaux ETS, pas un nombre d’appareils physiques.${assistantEnabled ? '' : '\n\nL’assistant IA n’est pas encore activé ; Setup Doctor indique ce qui manque.'}\n\nVous pouvez commencer sans risque avec :\n${prompts.map(item => `• ${item.text}`).join('\n')}` : `Bonjour${name ? ` ${name}` : ''} ! Aucune adresse ETS n’est encore sélectionnée pour KNX AI. Configurez l’accès aux objets ETS, vérifiez l’import CSV puis rouvrez Setup Doctor.`
645
+ prompts: { area: name => `Lecture seule : que savez-vous de « ${name} » ?`, inventory: 'Qu’avez-vous reconnu dans mon installation KNX ? Lecture seule.', lights: 'Quelles lumières pouvez-vous lire ? Ne changez rien.', openings: 'Quelles portes ou fenêtres sont ouvertes ? Lecture seule.', climate: 'Quels états de température et de climat pouvez-vous lire ?', anomalies: 'Des anomalies KNX demandent-elles attention ? Lecture seule.', setup: 'Que manque-t-il à ma configuration Cerebrum Ultimate ?' },
646
+ welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0 ? `Bonjour${name ? ` ${name}` : ''} ! Je me suis déjà orienté dans le projet ETS sans rien envoyer sur le bus. J’ai trouvé ${totals.groupAddresses} adresses de groupe uniques, ${totals.etsAreas} zones/groupes ETS et environ ${totals.logicalFunctionsEstimate} fonctions logiques reconnaissables. Ce sont des signaux ETS, pas un nombre d’appareils physiques.${assistantEnabled ? '' : '\n\nL’assistant IA n’est pas encore activé ; Setup Doctor indique ce qui manque.'}\n\nVous pouvez commencer sans risque avec :\n${prompts.map(item => `• ${item.text}`).join('\n')}` : `Bonjour${name ? ` ${name}` : ''} ! Aucune adresse ETS n’est encore sélectionnée pour Cerebrum Ultimate. Configurez l’accès aux objets ETS, vérifiez l’import CSV puis rouvrez Setup Doctor.`
647
647
  },
648
648
  es: {
649
649
  status: { ready: 'Listo', attention: 'Casi listo', blocked: 'Acción necesaria' },
650
650
  checks: {
651
651
  gateway: ['Gateway KNX', details => !details.configured ? 'Selecciona un gateway KNX Ultimate y vuelve a desplegar.' : details.connected ? `Conectado a ${details.name || 'el gateway configurado'}.` : `${details.name || 'El gateway configurado'} todavía no está conectado.`],
652
- ets: ['Proyecto ETS', details => details.objectCount > 0 ? `${details.objectCount} direcciones de grupo únicas y ${details.areaCount} áreas/grupos ETS reconocidos.` : 'No hay direcciones ETS disponibles para KNX AI. Configura el acceso a objetos ETS y verifica la importación CSV en la pasarela.'],
652
+ ets: ['Proyecto ETS', details => details.objectCount > 0 ? `${details.objectCount} direcciones de grupo únicas y ${details.areaCount} áreas/grupos ETS reconocidos.` : 'No hay direcciones ETS disponibles para Cerebrum Ultimate. Configura el acceso a objetos ETS y verifica la importación CSV en la pasarela.'],
653
653
  assistant: ['Asistente IA', details => details.enabled ? 'El asistente está habilitado.' : 'Habilita el asistente LLM para iniciar conversaciones.'],
654
654
  provider: ['Proveedor y modelo', details => details.ready ? `${details.providerLabel} · ${details.model}` : `Completa los ajustes que faltan: ${details.missing.join(', ')}.`],
655
655
  providerConnection: ['Conexión del proveedor', details => details.state === 'reachable' ? details.selectedModelAvailable === false ? `Proveedor accesible, pero el modelo seleccionado “${details.model}” no aparece en su catálogo.` : `Proveedor alcanzado correctamente${details.modelCount > 0 ? `; ${details.modelCount} modelo(s) disponibles` : ''}.` : details.state === 'checking' ? 'Comprobando el proveedor sin enviar una solicitud de chat…' : details.state === 'unreachable' ? 'La configuración está guardada, pero el endpoint de modelos no respondió. Actualiza los modelos para reintentar.' : 'La comprobación comenzará después de configurar el proveedor.'],
@@ -663,14 +663,14 @@ const getKnxAiSetupDoctorCopy = (language) => {
663
663
  homeAssistant: ['Home Assistant', details => details.ready ? 'Listo: Cerebrum y ha-api están conectados en un circuito completo de solicitud y respuesta.' : details.recommendationCode === 'add_ha_api' ? 'Node-RED funciona como add-on de Home Assistant, pero no hay un nodo API (ha-api) desplegado.' : details.recommendationCode === 'add_cerebrum_bridge' ? 'ha-api está presente. Añade el nodo Cerebrum Home Assistant.' : details.recommendationCode === 'wire_round_trip' ? 'Conecta Cerebrum Home Assistant → ha-api → Cerebrum Home Assistant.' : 'No se detectó Home Assistant; esta integración es opcional.']
664
664
  },
665
665
  summary: (status, totals, issueCount) => status === 'ready' ? `Listo: ${totals.groupAddresses} señales KNX, ${totals.etsAreas} áreas/grupos ETS y unas ${totals.logicalFunctionsEstimate} funciones lógicas reconocibles.` : status === 'attention' ? `Casi listo: ${totals.groupAddresses} señales KNX reconocidas; ${issueCount} elemento(s) requieren atención.` : `${totals.groupAddresses} señales KNX reconocidas, pero faltan ${issueCount} elemento(s) necesarios.`,
666
- prompts: { area: name => `Solo lectura: ¿qué sabes de «${name}»?`, inventory: '¿Qué reconoces en mi instalación KNX? Solo lectura.', lights: '¿Qué luces puedes leer ahora? No cambies nada.', openings: '¿Qué puertas o ventanas están abiertas? Solo lectura.', climate: '¿Qué temperaturas y estados del clima puedes leer?', anomalies: '¿Hay anomalías KNX que atender? Solo lectura.', setup: '¿Qué falta en mi configuración de KNX AI?' },
667
- welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0 ? `¡Hola${name ? ` ${name}` : ''}! Ya me he orientado en el proyecto ETS sin enviar nada al bus. Encontré ${totals.groupAddresses} direcciones de grupo únicas, ${totals.etsAreas} áreas/grupos ETS y unas ${totals.logicalFunctionsEstimate} funciones lógicas reconocibles. Son señales ETS, no un recuento de dispositivos físicos.${assistantEnabled ? '' : '\n\nEl asistente IA aún no está habilitado; Setup Doctor muestra lo que falta.'}\n\nPuedes empezar de forma segura con:\n${prompts.map(item => `• ${item.text}`).join('\n')}` : `¡Hola${name ? ` ${name}` : ''}! Aún no hay direcciones ETS seleccionadas para KNX AI. Configura el acceso a objetos ETS, verifica la importación CSV y vuelve a abrir Setup Doctor.`
666
+ prompts: { area: name => `Solo lectura: ¿qué sabes de «${name}»?`, inventory: '¿Qué reconoces en mi instalación KNX? Solo lectura.', lights: '¿Qué luces puedes leer ahora? No cambies nada.', openings: '¿Qué puertas o ventanas están abiertas? Solo lectura.', climate: '¿Qué temperaturas y estados del clima puedes leer?', anomalies: '¿Hay anomalías KNX que atender? Solo lectura.', setup: '¿Qué falta en mi configuración de Cerebrum Ultimate?' },
667
+ welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0 ? `¡Hola${name ? ` ${name}` : ''}! Ya me he orientado en el proyecto ETS sin enviar nada al bus. Encontré ${totals.groupAddresses} direcciones de grupo únicas, ${totals.etsAreas} áreas/grupos ETS y unas ${totals.logicalFunctionsEstimate} funciones lógicas reconocibles. Son señales ETS, no un recuento de dispositivos físicos.${assistantEnabled ? '' : '\n\nEl asistente IA aún no está habilitado; Setup Doctor muestra lo que falta.'}\n\nPuedes empezar de forma segura con:\n${prompts.map(item => `• ${item.text}`).join('\n')}` : `¡Hola${name ? ` ${name}` : ''}! Aún no hay direcciones ETS seleccionadas para Cerebrum Ultimate. Configura el acceso a objetos ETS, verifica la importación CSV y vuelve a abrir Setup Doctor.`
668
668
  },
669
669
  zh: {
670
670
  status: { ready: '已就绪', attention: '即将就绪', blocked: '需要处理' },
671
671
  checks: {
672
672
  gateway: ['KNX 网关', details => !details.configured ? '请选择 KNX Ultimate 网关并重新部署。' : details.connected ? `已连接到 ${details.name || '已配置网关'}。` : `${details.name || '已配置网关'}尚未连接。`],
673
- ets: ['ETS 项目', details => details.objectCount > 0 ? `已识别 ${details.objectCount} 个唯一组地址和 ${details.areaCount} 个 ETS 区域/组。` : 'KNX AI 没有可用的 ETS 组地址。请配置 ETS 对象访问并检查网关中的 ETS CSV 导入。'],
673
+ ets: ['ETS 项目', details => details.objectCount > 0 ? `已识别 ${details.objectCount} 个唯一组地址和 ${details.areaCount} 个 ETS 区域/组。` : 'Cerebrum Ultimate 没有可用的 ETS 组地址。请配置 ETS 对象访问并检查网关中的 ETS CSV 导入。'],
674
674
  assistant: ['AI 助手', details => details.enabled ? '助手已启用。' : '请启用 LLM 助手以开始对话。'],
675
675
  provider: ['提供商和模型', details => details.ready ? `${details.providerLabel} · ${details.model}` : `请补全缺少的设置:${details.missing.join('、')}。`],
676
676
  providerConnection: ['提供商连接', details => details.state === 'reachable' ? details.selectedModelAvailable === false ? `已连接提供商,但其目录中没有所选模型“${details.model}”。` : `已成功连接提供商${details.modelCount > 0 ? `;报告 ${details.modelCount} 个模型` : ''}。` : details.state === 'checking' ? '正在检查提供商,不会发送聊天请求…' : details.state === 'unreachable' ? '配置已保存,但模型端点没有响应。请刷新模型后重试。' : '配置提供商后将自动检查连接。'],
@@ -684,8 +684,8 @@ const getKnxAiSetupDoctorCopy = (language) => {
684
684
  homeAssistant: ['Home Assistant', details => details.ready ? '已就绪:Cerebrum 与 ha-api 已形成完整请求/响应回路。' : details.recommendationCode === 'add_ha_api' ? 'Node-RED 作为 Home Assistant add-on 运行,但流程中没有 API 节点(ha-api)。' : details.recommendationCode === 'add_cerebrum_bridge' ? '已存在 ha-api。请添加 Cerebrum Home Assistant 节点。' : details.recommendationCode === 'wire_round_trip' ? '请连接 Cerebrum Home Assistant → ha-api → Cerebrum Home Assistant。' : '未检测到 Home Assistant;此集成为可选项。']
685
685
  },
686
686
  summary: (status, totals, issueCount) => status === 'ready' ? `已就绪:${totals.groupAddresses} 个 KNX 信号、${totals.etsAreas} 个 ETS 区域/组,以及约 ${totals.logicalFunctionsEstimate} 个可识别逻辑功能。` : status === 'attention' ? `即将就绪:已识别 ${totals.groupAddresses} 个 KNX 信号;${issueCount} 项需要注意。` : `已识别 ${totals.groupAddresses} 个 KNX 信号,但仍需完成 ${issueCount} 个必要项目。`,
687
- prompts: { area: name => `只读:你了解“${name}”区域的哪些内容?`, inventory: '你在 KNX 系统中识别到了什么?仅限读取。', lights: '你现在可以读取哪些灯?不要更改任何内容。', openings: '目前哪些门或窗打开?仅限读取。', climate: '你现在可以读取哪些温度和空调状态?', anomalies: '是否有需要注意的 KNX 异常?仅限读取。', setup: '我的 KNX AI 配置还缺少什么?' },
688
- welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0 ? `你好${name ? `,${name}` : ''}!我已经了解 ETS 项目,且没有向总线发送任何内容。我发现了 ${totals.groupAddresses} 个唯一组地址、${totals.etsAreas} 个 ETS 区域/组,以及约 ${totals.logicalFunctionsEstimate} 个可识别逻辑功能。这些是 ETS 信号,并非物理设备数量。${assistantEnabled ? '' : '\n\nAI 助手尚未启用;Setup Doctor 会显示仍需配置的内容。'}\n\n你可以安全地从以下问题开始:\n${prompts.map(item => `• ${item.text}`).join('\n')}` : `你好${name ? `,${name}` : ''}!尚未为 KNX AI 选择 ETS 组地址。请配置 ETS 对象访问、检查 CSV 导入,然后重新打开 Setup Doctor。`
687
+ prompts: { area: name => `只读:你了解“${name}”区域的哪些内容?`, inventory: '你在 KNX 系统中识别到了什么?仅限读取。', lights: '你现在可以读取哪些灯?不要更改任何内容。', openings: '目前哪些门或窗打开?仅限读取。', climate: '你现在可以读取哪些温度和空调状态?', anomalies: '是否有需要注意的 KNX 异常?仅限读取。', setup: '我的 Cerebrum Ultimate 配置还缺少什么?' },
688
+ welcome: ({ name, totals, prompts, assistantEnabled }) => totals.groupAddresses > 0 ? `你好${name ? `,${name}` : ''}!我已经了解 ETS 项目,且没有向总线发送任何内容。我发现了 ${totals.groupAddresses} 个唯一组地址、${totals.etsAreas} 个 ETS 区域/组,以及约 ${totals.logicalFunctionsEstimate} 个可识别逻辑功能。这些是 ETS 信号,并非物理设备数量。${assistantEnabled ? '' : '\n\nAI 助手尚未启用;Setup Doctor 会显示仍需配置的内容。'}\n\n你可以安全地从以下问题开始:\n${prompts.map(item => `• ${item.text}`).join('\n')}` : `你好${name ? `,${name}` : ''}!尚未为 Cerebrum Ultimate 选择 ETS 组地址。请配置 ETS 对象访问、检查 CSV 导入,然后重新打开 Setup Doctor。`
689
689
  }
690
690
  }
691
691
  const code = normalizeLanguageCode(language, 'en')
@@ -983,7 +983,7 @@ const releaseSharedKnxAiState = ({ registry, filePath, node }) => {
983
983
  }
984
984
 
985
985
  // ---------------------------------------------------------------------------
986
- // KNX AI Flow Builder helpers
986
+ // Cerebrum Ultimate Flow Builder helpers
987
987
  // Build a node "catalog" (type + editable fields) from this package's own
988
988
  // editor .html files, so the LLM knows exactly which node types and config
989
989
  // fields it can emit when generating a Node-RED flow to paste in the editor.
@@ -1339,7 +1339,7 @@ const normalizeKnxAiGeneratedFlow = ({ rawNodes, catalog, knxServerId, existingC
1339
1339
  return node
1340
1340
  })
1341
1341
 
1342
- const tabNode = { id: tabId, type: 'tab', label: 'KNX AI generated flow', disabled: false, info: '' }
1342
+ const tabNode = { id: tabId, type: 'tab', label: 'Cerebrum Ultimate generated flow', disabled: false, info: '' }
1343
1343
  return { nodes: [tabNode].concat(out), warnings, tabId }
1344
1344
  }
1345
1345
 
@@ -1347,7 +1347,7 @@ const sendKnxAiVueIndex = (req, res) => {
1347
1347
  const entryPath = path.join(knxAiVueDistDir, 'index.html')
1348
1348
  fs.readFile(entryPath, 'utf8', (error, html) => {
1349
1349
  if (error || typeof html !== 'string') {
1350
- res.status(503).type('text/plain').send('KNX AI Vue build not found. Run "npm run knx-ai:build" in the module root.')
1350
+ res.status(503).type('text/plain').send('Cerebrum Ultimate Vue build not found. Run "npm run knx-ai:build" in the module root.')
1351
1351
  return
1352
1352
  }
1353
1353
  const rawToken = getRequestAccessToken(req)
@@ -2003,12 +2003,12 @@ const getKnxAiHabitCopy = language => {
2003
2003
 
2004
2004
  const getKnxAiBootFallbackCopy = ({ language, reason = '' } = {}) => {
2005
2005
  const copies = {
2006
- en: 'KNX AI has started and Cerebrum is supervising the home. The AI startup test could not generate this message',
2007
- it: 'KNX AI è stato avviato e Cerebrum mantiene la casa sotto supervisione. Il test AI di avvio non ha potuto generare questo messaggio',
2008
- de: 'KNX AI wurde gestartet und Cerebrum überwacht das Zuhause. Der KI-Starttest konnte diese Nachricht nicht erzeugen',
2009
- fr: 'KNX AI a démarré et Cerebrum supervise la maison. Le test IA de démarrage n’a pas pu générer ce message',
2010
- es: 'KNX AI se ha iniciado y Cerebrum supervisa la casa. La prueba de IA de inicio no pudo generar este mensaje',
2011
- zh: 'KNX AI 已启动,Cerebrum 正在监护住宅。启动时的 AI 测试未能生成此消息'
2006
+ en: 'Cerebrum Ultimate has started and Cerebrum is supervising the home. The AI startup test could not generate this message',
2007
+ it: 'Cerebrum Ultimate è stato avviato e Cerebrum mantiene la casa sotto supervisione. Il test AI di avvio non ha potuto generare questo messaggio',
2008
+ de: 'Cerebrum Ultimate wurde gestartet und Cerebrum überwacht das Zuhause. Der KI-Starttest konnte diese Nachricht nicht erzeugen',
2009
+ fr: 'Cerebrum Ultimate a démarré et Cerebrum supervise la maison. Le test IA de démarrage n’a pas pu générer ce message',
2010
+ es: 'Cerebrum Ultimate se ha iniciado y Cerebrum supervisa la casa. La prueba de IA de inicio no pudo generar este mensaje',
2011
+ zh: 'Cerebrum Ultimate 已启动,Cerebrum 正在监护住宅。启动时的 AI 测试未能生成此消息'
2012
2012
  }
2013
2013
  const normalized = normalizeHomeLanguage(language)
2014
2014
  const base = copies[normalized === 'zh-CN' ? 'zh' : normalized] || copies.en
@@ -2397,7 +2397,7 @@ const cloneKnxAiInputMessage = (inputMessage, cloneMessage, onError) => {
2397
2397
 
2398
2398
  const safeKnxAiSend = ({ outputs, send, onError }) => {
2399
2399
  try {
2400
- if (typeof send !== 'function') throw new Error('KNX AI output sender is unavailable')
2400
+ if (typeof send !== 'function') throw new Error('Cerebrum Ultimate output sender is unavailable')
2401
2401
  send(outputs)
2402
2402
  return true
2403
2403
  } catch (error) {
@@ -2420,7 +2420,7 @@ const compileKnxAiChatAdapter = ({ code, direction = 'chat' } = {}) => {
2420
2420
  run: new Function('msg', 'inputMessage', 'node', 'RED', `"use strict";\n${source}`) // eslint-disable-line no-new-func
2421
2421
  }
2422
2422
  } catch (error) {
2423
- throw new Error(`Invalid KNX AI ${adapterDirection} adapter: ${error.message || error}`)
2423
+ throw new Error(`Invalid Cerebrum Ultimate ${adapterDirection} adapter: ${error.message || error}`)
2424
2424
  }
2425
2425
  }
2426
2426
 
@@ -2434,11 +2434,11 @@ const executeKnxAiChatAdapter = ({
2434
2434
  if (!adapter || typeof adapter.run !== 'function') return msg
2435
2435
  const result = adapter.run(msg, inputMessage, node, RED)
2436
2436
  if (result && typeof result.then === 'function') {
2437
- throw new Error(`KNX AI ${adapter.direction || 'chat'} adapter must be synchronous`)
2437
+ throw new Error(`Cerebrum Ultimate ${adapter.direction || 'chat'} adapter must be synchronous`)
2438
2438
  }
2439
2439
  if (result === undefined || result === null) return null
2440
2440
  if (!result || typeof result !== 'object' || Array.isArray(result)) {
2441
- throw new Error(`KNX AI ${adapter.direction || 'chat'} adapter must return msg or no value`)
2441
+ throw new Error(`Cerebrum Ultimate ${adapter.direction || 'chat'} adapter must return msg or no value`)
2442
2442
  }
2443
2443
  return result
2444
2444
  }
@@ -5402,7 +5402,7 @@ const decorateChatCompletionsModelError = ({ error, model, url }) => {
5402
5402
  const decorated = new Error(
5403
5403
  `Model "${selectedModel}" is not compatible with the configured Chat Completions endpoint (${endpoint}). ` +
5404
5404
  'Choose a chat-capable model, for example gpt-5.4 or gpt-4o-mini. ' +
5405
- `Legacy /v1/completions models are not supported by KNX AI.${originalMessage ? ` Provider response: ${originalMessage}` : ''}`
5405
+ `Legacy /v1/completions models are not supported by Cerebrum Ultimate.${originalMessage ? ` Provider response: ${originalMessage}` : ''}`
5406
5406
  )
5407
5407
  if (error && error.status !== undefined) decorated.status = error.status
5408
5408
  if (error && error.response !== undefined) decorated.response = error.response
@@ -6074,7 +6074,7 @@ module.exports = function (RED) {
6074
6074
  }
6075
6075
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6076
6076
  if (!n || n.type !== 'knxUltimateAI' || typeof n.getSidebarState !== 'function') {
6077
- res.status(404).json({ error: 'KNX AI node not found' })
6077
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6078
6078
  return
6079
6079
  }
6080
6080
  const language = normalizeLanguageCode(req.query?.language || extractLanguageCodeFromHeader(req.headers && req.headers['accept-language']), 'en')
@@ -6096,7 +6096,7 @@ module.exports = function (RED) {
6096
6096
  }
6097
6097
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6098
6098
  if (!n || n.type !== 'knxUltimateAI' || typeof n.getChatLearningFile !== 'function') {
6099
- res.status(404).json({ error: 'KNX AI node not found' })
6099
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6100
6100
  return
6101
6101
  }
6102
6102
  res.json(await n.getChatLearningFile())
@@ -6114,7 +6114,7 @@ module.exports = function (RED) {
6114
6114
  }
6115
6115
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6116
6116
  if (!n || n.type !== 'knxUltimateAI' || typeof n.updateChatLearningFile !== 'function') {
6117
- res.status(404).json({ error: 'KNX AI node not found' })
6117
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6118
6118
  return
6119
6119
  }
6120
6120
  res.json(await n.updateChatLearningFile({
@@ -6135,7 +6135,7 @@ module.exports = function (RED) {
6135
6135
  }
6136
6136
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6137
6137
  if (!n || n.type !== 'knxUltimateAI' || typeof n.resetChatLearningFile !== 'function') {
6138
- res.status(404).json({ error: 'KNX AI node not found' })
6138
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6139
6139
  return
6140
6140
  }
6141
6141
  res.json(await n.resetChatLearningFile({ revision: req.body?.revision }))
@@ -6153,7 +6153,7 @@ module.exports = function (RED) {
6153
6153
  }
6154
6154
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6155
6155
  if (!n || n.type !== 'knxUltimateAI' || typeof n.getCerebrumMemoryFile !== 'function') {
6156
- res.status(404).json({ error: 'KNX AI node not found' })
6156
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6157
6157
  return
6158
6158
  }
6159
6159
  res.json(await n.getCerebrumMemoryFile())
@@ -6171,7 +6171,7 @@ module.exports = function (RED) {
6171
6171
  }
6172
6172
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6173
6173
  if (!n || n.type !== 'knxUltimateAI' || typeof n.updateCerebrumMemoryFile !== 'function') {
6174
- res.status(404).json({ error: 'KNX AI node not found' })
6174
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6175
6175
  return
6176
6176
  }
6177
6177
  res.json(await n.updateCerebrumMemoryFile({
@@ -6193,7 +6193,7 @@ module.exports = function (RED) {
6193
6193
  }
6194
6194
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6195
6195
  if (!n || n.type !== 'knxUltimateAI' || typeof n.resetCerebrumMemoryFile !== 'function') {
6196
- res.status(404).json({ error: 'KNX AI node not found' })
6196
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6197
6197
  return
6198
6198
  }
6199
6199
  res.json(await n.resetCerebrumMemoryFile({ revision: req.body?.revision }))
@@ -6216,7 +6216,7 @@ module.exports = function (RED) {
6216
6216
  }
6217
6217
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6218
6218
  if (!n || n.type !== 'knxUltimateAI' || typeof n.sidebarAsk !== 'function') {
6219
- res.status(404).json({ error: 'KNX AI node not found' })
6219
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6220
6220
  return
6221
6221
  }
6222
6222
  const ret = await n.sidebarAsk(question)
@@ -6240,7 +6240,7 @@ module.exports = function (RED) {
6240
6240
  }
6241
6241
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6242
6242
  if (!n || n.type !== 'knxUltimateAI' || typeof n.saveAreaDefinition !== 'function') {
6243
- res.status(404).json({ error: 'KNX AI node not found' })
6243
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6244
6244
  return
6245
6245
  }
6246
6246
  const ret = await n.saveAreaDefinition({
@@ -6270,7 +6270,7 @@ module.exports = function (RED) {
6270
6270
  }
6271
6271
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6272
6272
  if (!n || n.type !== 'knxUltimateAI' || typeof n.resetAreaDefinition !== 'function') {
6273
- res.status(404).json({ error: 'KNX AI node not found' })
6273
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6274
6274
  return
6275
6275
  }
6276
6276
  const ret = await n.resetAreaDefinition({ areaId })
@@ -6294,7 +6294,7 @@ module.exports = function (RED) {
6294
6294
  }
6295
6295
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6296
6296
  if (!n || n.type !== 'knxUltimateAI' || typeof n.deleteAreaDefinition !== 'function') {
6297
- res.status(404).json({ error: 'KNX AI node not found' })
6297
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6298
6298
  return
6299
6299
  }
6300
6300
  const ret = await n.deleteAreaDefinition({ areaId })
@@ -6313,7 +6313,7 @@ module.exports = function (RED) {
6313
6313
  }
6314
6314
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6315
6315
  if (!n || n.type !== 'knxUltimateAI' || typeof n.deleteAllLlmAreas !== 'function') {
6316
- res.status(404).json({ error: 'KNX AI node not found' })
6316
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6317
6317
  return
6318
6318
  }
6319
6319
  const ret = await n.deleteAllLlmAreas()
@@ -6332,7 +6332,7 @@ module.exports = function (RED) {
6332
6332
  }
6333
6333
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6334
6334
  if (!n || n.type !== 'knxUltimateAI' || typeof n.getGaCatalog !== 'function') {
6335
- res.status(404).json({ error: 'KNX AI node not found' })
6335
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6336
6336
  return
6337
6337
  }
6338
6338
  const ret = await n.getGaCatalog()
@@ -6351,7 +6351,7 @@ module.exports = function (RED) {
6351
6351
  }
6352
6352
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6353
6353
  if (!n || n.type !== 'knxUltimateAI' || typeof n.saveGaRoleOverride !== 'function') {
6354
- res.status(404).json({ error: 'KNX AI node not found' })
6354
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6355
6355
  return
6356
6356
  }
6357
6357
  const ret = await n.saveGaRoleOverride(req.body || {})
@@ -6370,7 +6370,7 @@ module.exports = function (RED) {
6370
6370
  }
6371
6371
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6372
6372
  if (!n || n.type !== 'knxUltimateAI' || typeof n.createAreaDefinition !== 'function') {
6373
- res.status(404).json({ error: 'KNX AI node not found' })
6373
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6374
6374
  return
6375
6375
  }
6376
6376
  const ret = await n.createAreaDefinition(req.body || {})
@@ -6389,7 +6389,7 @@ module.exports = function (RED) {
6389
6389
  }
6390
6390
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6391
6391
  if (!n || n.type !== 'knxUltimateAI' || typeof n.regenerateLlmAreas !== 'function') {
6392
- res.status(404).json({ error: 'KNX AI node not found' })
6392
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6393
6393
  return
6394
6394
  }
6395
6395
  const ret = await n.regenerateLlmAreas()
@@ -6408,7 +6408,7 @@ module.exports = function (RED) {
6408
6408
  }
6409
6409
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6410
6410
  if (!n || n.type !== 'knxUltimateAI' || typeof n.suggestAreaDraftWithLlm !== 'function') {
6411
- res.status(404).json({ error: 'KNX AI node not found' })
6411
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6412
6412
  return
6413
6413
  }
6414
6414
  const ret = await n.suggestAreaDraftWithLlm(req.body || {})
@@ -6427,7 +6427,7 @@ module.exports = function (RED) {
6427
6427
  }
6428
6428
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6429
6429
  if (!n || n.type !== 'knxUltimateAI' || typeof n.saveAreaProfile !== 'function') {
6430
- res.status(404).json({ error: 'KNX AI node not found' })
6430
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6431
6431
  return
6432
6432
  }
6433
6433
  const ret = await n.saveAreaProfile(req.body || {})
@@ -6451,7 +6451,7 @@ module.exports = function (RED) {
6451
6451
  }
6452
6452
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6453
6453
  if (!n || n.type !== 'knxUltimateAI' || typeof n.deleteAreaProfile !== 'function') {
6454
- res.status(404).json({ error: 'KNX AI node not found' })
6454
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6455
6455
  return
6456
6456
  }
6457
6457
  const ret = await n.deleteAreaProfile({ profileId })
@@ -6472,7 +6472,7 @@ module.exports = function (RED) {
6472
6472
  }
6473
6473
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6474
6474
  if (!n || n.type !== 'knxUltimateAI' || typeof n.runAreaProfile !== 'function') {
6475
- res.status(404).json({ error: 'KNX AI node not found' })
6475
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6476
6476
  return
6477
6477
  }
6478
6478
  const ret = await n.runAreaProfile({ areaId, profileId })
@@ -6491,7 +6491,7 @@ module.exports = function (RED) {
6491
6491
  }
6492
6492
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6493
6493
  if (!n || n.type !== 'knxUltimateAI' || typeof n.exportAiConfig !== 'function') {
6494
- res.status(404).json({ error: 'KNX AI node not found' })
6494
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6495
6495
  return
6496
6496
  }
6497
6497
  const ret = await n.exportAiConfig()
@@ -6511,7 +6511,7 @@ module.exports = function (RED) {
6511
6511
  }
6512
6512
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6513
6513
  if (!n || n.type !== 'knxUltimateAI' || typeof n.importAiConfig !== 'function') {
6514
- res.status(404).json({ error: 'KNX AI node not found' })
6514
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6515
6515
  return
6516
6516
  }
6517
6517
  const ret = await n.importAiConfig(configPayload)
@@ -6530,7 +6530,7 @@ module.exports = function (RED) {
6530
6530
  }
6531
6531
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6532
6532
  if (!n || n.type !== 'knxUltimateAI' || typeof n.saveActuatorTestPreset !== 'function') {
6533
- res.status(404).json({ error: 'KNX AI node not found' })
6533
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6534
6534
  return
6535
6535
  }
6536
6536
  const ret = await n.saveActuatorTestPreset(req.body || {})
@@ -6554,7 +6554,7 @@ module.exports = function (RED) {
6554
6554
  }
6555
6555
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6556
6556
  if (!n || n.type !== 'knxUltimateAI' || typeof n.deleteActuatorTestPreset !== 'function') {
6557
- res.status(404).json({ error: 'KNX AI node not found' })
6557
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6558
6558
  return
6559
6559
  }
6560
6560
  const ret = await n.deleteActuatorTestPreset({ presetId })
@@ -6573,7 +6573,7 @@ module.exports = function (RED) {
6573
6573
  }
6574
6574
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6575
6575
  if (!n || n.type !== 'knxUltimateAI' || typeof n.runActuatorTest !== 'function') {
6576
- res.status(404).json({ error: 'KNX AI node not found' })
6576
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6577
6577
  return
6578
6578
  }
6579
6579
  const ret = await n.runActuatorTest(req.body || {})
@@ -6597,7 +6597,7 @@ module.exports = function (RED) {
6597
6597
  }
6598
6598
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6599
6599
  if (!n || n.type !== 'knxUltimateAI' || typeof n.getAreaSignalCatalog !== 'function') {
6600
- res.status(404).json({ error: 'KNX AI node not found' })
6600
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6601
6601
  return
6602
6602
  }
6603
6603
  const ret = await n.getAreaSignalCatalog({ areaId })
@@ -6629,7 +6629,7 @@ module.exports = function (RED) {
6629
6629
  }
6630
6630
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6631
6631
  if (!n || n.type !== 'knxUltimateAI' || typeof n.generateAiTestPlan !== 'function') {
6632
- res.status(404).json({ error: 'KNX AI node not found' })
6632
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6633
6633
  return
6634
6634
  }
6635
6635
  const ret = await n.generateAiTestPlan({ areaId, prompt, language })
@@ -6656,7 +6656,7 @@ module.exports = function (RED) {
6656
6656
  }
6657
6657
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6658
6658
  if (!n || n.type !== 'knxUltimateAI' || typeof n.generateAiFlow !== 'function') {
6659
- res.status(404).json({ error: 'KNX AI node not found' })
6659
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6660
6660
  return
6661
6661
  }
6662
6662
  const ret = await n.generateAiFlow({ prompt, language })
@@ -6675,7 +6675,7 @@ module.exports = function (RED) {
6675
6675
  }
6676
6676
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6677
6677
  if (!n || n.type !== 'knxUltimateAI' || typeof n.saveAiTestPlan !== 'function') {
6678
- res.status(404).json({ error: 'KNX AI node not found' })
6678
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6679
6679
  return
6680
6680
  }
6681
6681
  const ret = await n.saveAiTestPlan(req.body?.plan || {})
@@ -6699,7 +6699,7 @@ module.exports = function (RED) {
6699
6699
  }
6700
6700
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6701
6701
  if (!n || n.type !== 'knxUltimateAI' || typeof n.deleteAiTestPlan !== 'function') {
6702
- res.status(404).json({ error: 'KNX AI node not found' })
6702
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6703
6703
  return
6704
6704
  }
6705
6705
  const ret = await n.deleteAiTestPlan({ planId })
@@ -6718,7 +6718,7 @@ module.exports = function (RED) {
6718
6718
  }
6719
6719
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6720
6720
  if (!n || n.type !== 'knxUltimateAI' || typeof n.runAiTestPlan !== 'function') {
6721
- res.status(404).json({ error: 'KNX AI node not found' })
6721
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6722
6722
  return
6723
6723
  }
6724
6724
  const ret = await n.runAiTestPlan({
@@ -6745,7 +6745,7 @@ module.exports = function (RED) {
6745
6745
  }
6746
6746
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6747
6747
  if (!n || n.type !== 'knxUltimateAI' || typeof n.runAiTestPlanStep !== 'function') {
6748
- res.status(404).json({ error: 'KNX AI node not found' })
6748
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6749
6749
  return
6750
6750
  }
6751
6751
  const ret = await n.runAiTestPlanStep({
@@ -6767,7 +6767,7 @@ module.exports = function (RED) {
6767
6767
  }
6768
6768
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6769
6769
  if (!n || n.type !== 'knxUltimateAI' || typeof n.saveAiTestResult !== 'function') {
6770
- res.status(404).json({ error: 'KNX AI node not found' })
6770
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6771
6771
  return
6772
6772
  }
6773
6773
  const ret = await n.saveAiTestResult(req.body?.report || {})
@@ -6791,7 +6791,7 @@ module.exports = function (RED) {
6791
6791
  }
6792
6792
  const n = aiRuntimeNodes.get(nodeId) || RED.nodes.getNode(nodeId)
6793
6793
  if (!n || n.type !== 'knxUltimateAI' || typeof n.deleteAiTestResult !== 'function') {
6794
- res.status(404).json({ error: 'KNX AI node not found' })
6794
+ res.status(404).json({ error: 'Cerebrum Ultimate node not found' })
6795
6795
  return
6796
6796
  }
6797
6797
  const ret = await n.deleteAiTestResult({ reportId })
@@ -7014,7 +7014,7 @@ module.exports = function (RED) {
7014
7014
  return
7015
7015
  }
7016
7016
 
7017
- node.name = config.name || 'KNX AI'
7017
+ node.name = config.name || 'Legacy AI node'
7018
7018
  node.topic = config.topic || node.name
7019
7019
  node.outputtopic = node.topic
7020
7020
  node.dpt = ''
@@ -7813,7 +7813,7 @@ module.exports = function (RED) {
7813
7813
  const fallback = String(csvGaLabels[ga] || '').trim()
7814
7814
  if (fallback) gaLabels[ga] = fallback
7815
7815
  })
7816
- gaLabels.BUS = node.name || 'KNX AI'
7816
+ gaLabels.BUS = node.name || 'Cerebrum Ultimate'
7817
7817
 
7818
7818
  let patterns = []
7819
7819
  if (node.enablePattern) {
@@ -8570,7 +8570,7 @@ module.exports = function (RED) {
8570
8570
  })
8571
8571
  const filePath = getLastChatPromptDebugFile()
8572
8572
  const content = [
8573
- 'KNX AI LAST CHAT PROMPT — LOCAL DEBUG COPY',
8573
+ 'Cerebrum Ultimate LAST CHAT PROMPT — LOCAL DEBUG COPY',
8574
8574
  `Generated at: ${new Date().toISOString()}`,
8575
8575
  `Provider: ${String(node.llmProvider || '')}`,
8576
8576
  `Model: ${String(node.llmModel || '')}`,
@@ -8606,7 +8606,7 @@ module.exports = function (RED) {
8606
8606
  try {
8607
8607
  writeAtomicUtf8File({ filePath: markdownPath, content: buildKnxAiScheduleMarkdown(node._scheduleStore) })
8608
8608
  } catch (markdownError) {
8609
- try { node.sysLogger?.warn(`KNX AI schedule Markdown write error: ${markdownError.message || markdownError}`) } catch (logError) { /* ignore */ }
8609
+ try { node.sysLogger?.warn(`Cerebrum Ultimate schedule Markdown write error: ${markdownError.message || markdownError}`) } catch (logError) { /* ignore */ }
8610
8610
  }
8611
8611
  node._scheduleStorePath = filePath
8612
8612
  return {
@@ -8616,7 +8616,7 @@ module.exports = function (RED) {
8616
8616
  activeCount: listActiveKnxAiSchedules(node._scheduleStore).length
8617
8617
  }
8618
8618
  } catch (error) {
8619
- try { node.sysLogger?.warn(`KNX AI schedule write error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8619
+ try { node.sysLogger?.warn(`Cerebrum Ultimate schedule write error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8620
8620
  return null
8621
8621
  }
8622
8622
  }
@@ -8657,7 +8657,7 @@ module.exports = function (RED) {
8657
8657
  scheduleScheduleStorePersist({ immediate: true })
8658
8658
  }
8659
8659
  } catch (recoveryError) { /* preserve the original load error */ }
8660
- try { node.sysLogger?.warn(`KNX AI schedule load error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8660
+ try { node.sysLogger?.warn(`Cerebrum Ultimate schedule load error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8661
8661
  return null
8662
8662
  }
8663
8663
  }
@@ -8674,7 +8674,7 @@ module.exports = function (RED) {
8674
8674
  try { fs.unlinkSync(path.join(dirPath, name)) } catch (error) { /* ignore */ }
8675
8675
  })
8676
8676
  } catch (error) {
8677
- try { node.sysLogger?.warn(`KNX AI home memory temporary-file cleanup error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8677
+ try { node.sysLogger?.warn(`Cerebrum Ultimate home memory temporary-file cleanup error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8678
8678
  }
8679
8679
  }
8680
8680
 
@@ -8737,7 +8737,7 @@ module.exports = function (RED) {
8737
8737
  maxBytes: rendered.maxBytes
8738
8738
  }
8739
8739
  } catch (error) {
8740
- try { node.sysLogger?.warn(`KNX AI home memory write error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8740
+ try { node.sysLogger?.warn(`Cerebrum Ultimate home memory write error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8741
8741
  return null
8742
8742
  }
8743
8743
  }
@@ -8798,7 +8798,7 @@ module.exports = function (RED) {
8798
8798
  property: '_homeMemory',
8799
8799
  initialValue: createEmptyKnxAiHomeMemory()
8800
8800
  })
8801
- try { node.sysLogger?.warn(`KNX AI home memory load error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8801
+ try { node.sysLogger?.warn(`Cerebrum Ultimate home memory load error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8802
8802
  return scheduleHomeMemoryPersist({ immediate: true })
8803
8803
  }
8804
8804
  }
@@ -8899,7 +8899,7 @@ module.exports = function (RED) {
8899
8899
  try { fs.unlinkSync(path.join(dirPath, name)) } catch (error) { /* ignore */ }
8900
8900
  })
8901
8901
  } catch (error) {
8902
- try { node.sysLogger?.warn(`KNX AI chat context temporary-file cleanup error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8902
+ try { node.sysLogger?.warn(`Cerebrum Ultimate chat context temporary-file cleanup error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8903
8903
  }
8904
8904
  }
8905
8905
 
@@ -8928,7 +8928,7 @@ module.exports = function (RED) {
8928
8928
  maxBytes: rendered.maxBytes
8929
8929
  }
8930
8930
  } catch (error) {
8931
- try { node.sysLogger?.warn(`KNX AI chat context write error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8931
+ try { node.sysLogger?.warn(`Cerebrum Ultimate chat context write error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8932
8932
  return null
8933
8933
  }
8934
8934
  }
@@ -8992,7 +8992,7 @@ module.exports = function (RED) {
8992
8992
  initialValue: createEmptyKnxAiChatContext()
8993
8993
  })
8994
8994
  node._conversationSessions = new Map()
8995
- try { node.sysLogger?.warn(`KNX AI chat context load error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8995
+ try { node.sysLogger?.warn(`Cerebrum Ultimate chat context load error: ${error.message || error}`) } catch (logError) { /* ignore */ }
8996
8996
  return scheduleChatContextPersist({ immediate: true })
8997
8997
  }
8998
8998
  }
@@ -9055,7 +9055,7 @@ module.exports = function (RED) {
9055
9055
  node._chatContext = rendered.context
9056
9056
  node._conversationSessions = conversationMapFromKnxAiChatContext(node._chatContext)
9057
9057
  const persisted = scheduleChatContextPersist({ immediate: true })
9058
- if (!persisted) throw new Error('Unable to save the KNX AI chat-learning file')
9058
+ if (!persisted) throw new Error('Unable to save the Cerebrum Ultimate chat-learning file')
9059
9059
  return buildChatLearningFileSnapshot({ fromDisk: true })
9060
9060
  }
9061
9061
 
@@ -9078,7 +9078,7 @@ module.exports = function (RED) {
9078
9078
  boundNode._chatSessionSources = new Map()
9079
9079
  })
9080
9080
  const persisted = scheduleChatContextPersist({ immediate: true })
9081
- if (!persisted) throw new Error('Unable to reinitialize the KNX AI chat-learning file')
9081
+ if (!persisted) throw new Error('Unable to reinitialize the Cerebrum Ultimate chat-learning file')
9082
9082
  return buildChatLearningFileSnapshot({ fromDisk: true })
9083
9083
  }
9084
9084
 
@@ -9148,7 +9148,7 @@ module.exports = function (RED) {
9148
9148
  }
9149
9149
  }
9150
9150
  } catch (error) {
9151
- node.sysLogger?.warn(`KNX AI history prune error: ${error.message || error}`)
9151
+ node.sysLogger?.warn(`Cerebrum Ultimate history prune error: ${error.message || error}`)
9152
9152
  }
9153
9153
  }
9154
9154
 
@@ -9163,7 +9163,7 @@ module.exports = function (RED) {
9163
9163
  if (pendingKey) node._historyDiskPending.set(pendingKey, telegram)
9164
9164
  fs.appendFile(filePath, line, 'utf8', (error) => {
9165
9165
  if (pendingKey && node._historyDiskPending.get(pendingKey) === telegram) node._historyDiskPending.delete(pendingKey)
9166
- if (error) node.sysLogger?.warn(`KNX AI history append error: ${error.message || error}`)
9166
+ if (error) node.sysLogger?.warn(`Cerebrum Ultimate history append error: ${error.message || error}`)
9167
9167
  })
9168
9168
  pruneHistoryArchiveFiles()
9169
9169
  }
@@ -9198,7 +9198,7 @@ module.exports = function (RED) {
9198
9198
  node._history = restored
9199
9199
  trimHistory(now)
9200
9200
  } catch (error) {
9201
- node.sysLogger?.warn(`KNX AI history restore error: ${error.message || error}`)
9201
+ node.sysLogger?.warn(`Cerebrum Ultimate history restore error: ${error.message || error}`)
9202
9202
  }
9203
9203
  }
9204
9204
 
@@ -9238,7 +9238,7 @@ module.exports = function (RED) {
9238
9238
  })
9239
9239
  return accumulator.finish()
9240
9240
  } catch (error) {
9241
- node.sysLogger?.warn(`KNX AI history load slice error: ${error.message || error}`)
9241
+ node.sysLogger?.warn(`Cerebrum Ultimate history load slice error: ${error.message || error}`)
9242
9242
  return emptyAccumulator()
9243
9243
  }
9244
9244
  }
@@ -9314,7 +9314,7 @@ module.exports = function (RED) {
9314
9314
  try { fs.unlinkSync(path.join(dirPath, entry.name)) } catch (error) { /* ignore */ }
9315
9315
  })
9316
9316
  } catch (error) {
9317
- node.sysLogger?.warn(`KNX AI adapter history prune error: ${error.message || error}`)
9317
+ node.sysLogger?.warn(`Cerebrum Ultimate adapter history prune error: ${error.message || error}`)
9318
9318
  }
9319
9319
  }
9320
9320
 
@@ -9328,7 +9328,7 @@ module.exports = function (RED) {
9328
9328
  if (pendingKey) node._adapterHistoryDiskPending.set(pendingKey, normalized)
9329
9329
  fs.appendFile(filePath, `${serializeKnxAiCompactHistoryRecord(normalized, 'adapter')}\n`, 'utf8', error => {
9330
9330
  if (pendingKey && node._adapterHistoryDiskPending.get(pendingKey) === normalized) node._adapterHistoryDiskPending.delete(pendingKey)
9331
- if (error) node.sysLogger?.warn(`KNX AI adapter history append error: ${error.message || error}`)
9331
+ if (error) node.sysLogger?.warn(`Cerebrum Ultimate adapter history append error: ${error.message || error}`)
9332
9332
  })
9333
9333
  pruneAdapterHistoryArchiveFiles()
9334
9334
  return normalized
@@ -9365,7 +9365,7 @@ module.exports = function (RED) {
9365
9365
  if (Number.isFinite(ts) && ts >= from && ts <= to) accumulator.add(item)
9366
9366
  })
9367
9367
  } catch (error) {
9368
- node.sysLogger?.warn(`KNX AI adapter history load error: ${error.message || error}`)
9368
+ node.sysLogger?.warn(`Cerebrum Ultimate adapter history load error: ${error.message || error}`)
9369
9369
  }
9370
9370
  return accumulator.finish()
9371
9371
  }
@@ -9509,7 +9509,7 @@ module.exports = function (RED) {
9509
9509
  }
9510
9510
  const filePath = getAiConfigStorageFile()
9511
9511
  const dirPath = path.dirname(filePath)
9512
- if (!ensureDirectorySync(dirPath)) throw new Error('Unable to create KNX AI storage directory')
9512
+ if (!ensureDirectorySync(dirPath)) throw new Error('Unable to create Cerebrum Ultimate storage directory')
9513
9513
  fs.writeFileSync(filePath, JSON.stringify({
9514
9514
  version: 4,
9515
9515
  updatedAt: new Date().toISOString(),
@@ -10699,7 +10699,7 @@ module.exports = function (RED) {
10699
10699
  }
10700
10700
 
10701
10701
  node.regenerateLlmAreas = async () => {
10702
- if (!node.llmEnabled) throw new Error('LLM is disabled in the KNX AI node config')
10702
+ if (!node.llmEnabled) throw new Error('LLM is disabled in the Cerebrum Ultimate node config')
10703
10703
  const gaCatalog = getGaCatalogSnapshot()
10704
10704
  if (!Array.isArray(gaCatalog) || !gaCatalog.length) throw new Error('No ETS group addresses available')
10705
10705
  const jsonMaxTokens = Math.max(50000, Number(node.llmMaxTokens) || 0)
@@ -10741,7 +10741,7 @@ module.exports = function (RED) {
10741
10741
  writeGaRoleOverrides(nextGaRoles)
10742
10742
  }
10743
10743
  } catch (error) {
10744
- node.warn(`KNX AI role classification skipped: ${error.message || String(error)}`)
10744
+ node.warn(`Cerebrum Ultimate role classification skipped: ${error.message || String(error)}`)
10745
10745
  }
10746
10746
 
10747
10747
  const nextOverrides = Object.assign({}, loadAreaOverrides())
@@ -10778,7 +10778,7 @@ module.exports = function (RED) {
10778
10778
  }
10779
10779
 
10780
10780
  node.suggestAreaDraftWithLlm = async ({ prompt, name, description, gaList } = {}) => {
10781
- if (!node.llmEnabled) throw new Error('LLM is disabled in the KNX AI node config')
10781
+ if (!node.llmEnabled) throw new Error('LLM is disabled in the Cerebrum Ultimate node config')
10782
10782
  const installerPrompt = String(prompt || '').trim()
10783
10783
  if (!installerPrompt) throw new Error('Missing prompt')
10784
10784
  const gaCatalog = getGaCatalogSnapshot()
@@ -10819,7 +10819,7 @@ module.exports = function (RED) {
10819
10819
  writeGaRoleOverrides(nextGaRoles)
10820
10820
  }
10821
10821
  } catch (error) {
10822
- node.warn(`KNX AI draft GA role classification skipped: ${error.message || String(error)}`)
10822
+ node.warn(`Cerebrum Ultimate draft GA role classification skipped: ${error.message || String(error)}`)
10823
10823
  }
10824
10824
  return {
10825
10825
  ok: true,
@@ -11119,7 +11119,7 @@ module.exports = function (RED) {
11119
11119
 
11120
11120
  node.getChatLearningFile = async () => {
11121
11121
  const persisted = scheduleChatContextPersist({ immediate: true })
11122
- if (!persisted) throw new Error('Unable to prepare the KNX AI chat-learning file')
11122
+ if (!persisted) throw new Error('Unable to prepare the Cerebrum Ultimate chat-learning file')
11123
11123
  return buildChatLearningFileSnapshot({ fromDisk: true })
11124
11124
  }
11125
11125
 
@@ -11165,7 +11165,7 @@ module.exports = function (RED) {
11165
11165
  node.importAiConfig = async (payload) => {
11166
11166
  const p = payload && typeof payload === 'object' ? payload : {}
11167
11167
  if (p.format !== 'knx-ai-cerebrum-backup' || p.version !== 1) {
11168
- throw Object.assign(new Error('Unsupported backup. Import a KNX AI Cerebrum backup version 1.'), { status: 400 })
11168
+ throw Object.assign(new Error('Unsupported backup. Import a Cerebrum Ultimate Cerebrum backup version 1.'), { status: 400 })
11169
11169
  }
11170
11170
  const files = p.files && typeof p.files === 'object' && !Array.isArray(p.files) ? p.files : null
11171
11171
  const readBackupContent = (id, maxBytes) => {
@@ -11197,7 +11197,7 @@ module.exports = function (RED) {
11197
11197
  readBackupContent('schedulesReadable', 2 * 1024 * 1024)
11198
11198
  } catch (error) {
11199
11199
  if (error && error.status) throw error
11200
- throw Object.assign(new Error(`Invalid KNX AI Cerebrum backup: ${error.message || error}`), { status: 400 })
11200
+ throw Object.assign(new Error(`Invalid Cerebrum Ultimate Cerebrum backup: ${error.message || error}`), { status: 400 })
11201
11201
  }
11202
11202
 
11203
11203
  const nextAreas = configuration.areas && typeof configuration.areas === 'object' ? configuration.areas : {}
@@ -11427,7 +11427,7 @@ module.exports = function (RED) {
11427
11427
  userContent: resolvedUserContent
11428
11428
  })
11429
11429
  } catch (error) {
11430
- try { node.sysLogger?.warn(`KNX AI prompt debug file error: ${error.message || error}`) } catch (logError) { /* ignore */ }
11430
+ try { node.sysLogger?.warn(`Cerebrum Ultimate prompt debug file error: ${error.message || error}`) } catch (logError) { /* ignore */ }
11431
11431
  }
11432
11432
  }
11433
11433
  const structuredSchema = jsonSchema && jsonSchema.schema
@@ -11838,7 +11838,7 @@ module.exports = function (RED) {
11838
11838
  configLines.length ? configLines.join('\n') : '(none found)',
11839
11839
  '',
11840
11840
  `CONFIGURED KNX GROUP ADDRESS CATALOG (${fullGaCatalog.length} objects; ga | dpt | access | label):`,
11841
- gaLines.length ? gaLines.join('\n') : '(no ETS group address selected for KNX AI)',
11841
+ gaLines.length ? gaLines.join('\n') : '(no ETS group address selected for Cerebrum Ultimate)',
11842
11842
  '',
11843
11843
  'Return the JSON object now.'
11844
11844
  ].join('\n')
@@ -12692,7 +12692,7 @@ module.exports = function (RED) {
12692
12692
  retrievedAt: new Date().toISOString(),
12693
12693
  error: 'The bounded Web operation failed.'
12694
12694
  }))
12695
- try { node.sysLogger?.warn(`KNX AI Web operation error: ${error.message || error}`) } catch (logError) { /* ignore */ }
12695
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Web operation error: ${error.message || error}`) } catch (logError) { /* ignore */ }
12696
12696
  }
12697
12697
  }
12698
12698
  const blockedReason = node.webAccessEnabled === true
@@ -13019,7 +13019,7 @@ module.exports = function (RED) {
13019
13019
  fallback: 'text',
13020
13020
  error: error.message || String(error)
13021
13021
  }
13022
- try { node.sysLogger?.warn(`KNX AI Telegram voice reply fallback: ${error.message || error}`) } catch (logError) { /* ignore */ }
13022
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Telegram voice reply fallback: ${error.message || error}`) } catch (logError) { /* ignore */ }
13023
13023
  }
13024
13024
  return enriched
13025
13025
  }
@@ -13785,7 +13785,7 @@ module.exports = function (RED) {
13785
13785
  if (typeof provider.subscribe === 'function') {
13786
13786
  const unsubscribe = provider.subscribe(event => {
13787
13787
  try { handleCameraAdapterEvent(event, provider) } catch (error) {
13788
- try { node.sysLogger?.warn(`KNX AI camera event error: ${error.message || error}`) } catch (logError) { /* ignore */ }
13788
+ try { node.sysLogger?.warn(`Cerebrum Ultimate camera event error: ${error.message || error}`) } catch (logError) { /* ignore */ }
13789
13789
  }
13790
13790
  })
13791
13791
  node._cameraProviderUnsubscribers.set(providerId, typeof unsubscribe === 'function' ? unsubscribe : () => {})
@@ -13810,7 +13810,7 @@ module.exports = function (RED) {
13810
13810
  controllerName: camera && camera.controllerName || provider.controllerName
13811
13811
  }))).filter(Boolean)
13812
13812
  } catch (error) {
13813
- try { node.sysLogger?.warn(`KNX AI camera catalog '${providerId}' unavailable: ${error.message || error}`) } catch (logError) { /* ignore */ }
13813
+ try { node.sysLogger?.warn(`Cerebrum Ultimate camera catalog '${providerId}' unavailable: ${error.message || error}`) } catch (logError) { /* ignore */ }
13814
13814
  return []
13815
13815
  }
13816
13816
  }))
@@ -13904,7 +13904,7 @@ module.exports = function (RED) {
13904
13904
  if (typeof provider.subscribe === 'function') {
13905
13905
  const unsubscribe = provider.subscribe(event => {
13906
13906
  try { handleHomeAutomationAdapterEvent(event, provider) } catch (error) {
13907
- try { node.sysLogger?.warn(`KNX AI home automation event error: ${error.message || error}`) } catch (logError) { /* ignore */ }
13907
+ try { node.sysLogger?.warn(`Cerebrum Ultimate home automation event error: ${error.message || error}`) } catch (logError) { /* ignore */ }
13908
13908
  }
13909
13909
  })
13910
13910
  node._homeAutomationProviderUnsubscribers.set(providerId, typeof unsubscribe === 'function' ? unsubscribe : () => {})
@@ -13980,7 +13980,7 @@ module.exports = function (RED) {
13980
13980
  lastError: error.message || String(error)
13981
13981
  })
13982
13982
  scheduleHomeMemoryPersist()
13983
- try { node.sysLogger?.warn(`KNX AI Cerebrum Home Assistant refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
13983
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Cerebrum Home Assistant refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
13984
13984
  return false
13985
13985
  }
13986
13986
  }
@@ -14073,12 +14073,12 @@ module.exports = function (RED) {
14073
14073
  systemPrompt: [
14074
14074
  'Write a warm, concise startup notification for a smart-home assistant.',
14075
14075
  `Use language ${normalizedLanguage}.`,
14076
- 'Say explicitly that the KNX AI node has started and reassure the occupant that the home is under Cerebrum supervision.',
14076
+ 'Say explicitly that the Cerebrum Ultimate node has started and reassure the occupant that the home is under Cerebrum supervision.',
14077
14077
  'This is also a live AI startup test. Do not claim that integrations or devices were checked, that every service is online, or that any home action was performed.',
14078
14078
  'Use one or two natural sentences. Do not include Markdown, IDs, addresses, DPTs or technical diagnostics.',
14079
14079
  'Return JSON only with exactly: {"message":"text"}.'
14080
14080
  ].join('\n'),
14081
- userContent: 'Generate the KNX AI startup notification now.',
14081
+ userContent: 'Generate the Cerebrum Ultimate startup notification now.',
14082
14082
  jsonSchema: {
14083
14083
  name: 'knx_ai_boot_notification',
14084
14084
  strict: true,
@@ -14127,7 +14127,7 @@ module.exports = function (RED) {
14127
14127
  } catch (error) {
14128
14128
  llmError = String(error && error.message || error || '').replace(/\s+/g, ' ').trim().slice(0, 300)
14129
14129
  content = getKnxAiBootFallbackCopy({ language })
14130
- try { node.sysLogger?.warn(`KNX AI startup notification model test failed: ${error.message || error}`) } catch (logError) { /* ignore */ }
14130
+ try { node.sysLogger?.warn(`Cerebrum Ultimate startup notification model test failed: ${error.message || error}`) } catch (logError) { /* ignore */ }
14131
14131
  } finally {
14132
14132
  node._bootAssistantInFlight = false
14133
14133
  }
@@ -14287,7 +14287,7 @@ module.exports = function (RED) {
14287
14287
  scheduleHomeMemoryPersist({ immediate: true })
14288
14288
  return true
14289
14289
  } catch (error) {
14290
- try { node.sysLogger?.warn(`KNX AI Cerebrum habit proposal error: ${error.message || error}`) } catch (logError) { /* ignore */ }
14290
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Cerebrum habit proposal error: ${error.message || error}`) } catch (logError) { /* ignore */ }
14291
14291
  return false
14292
14292
  } finally {
14293
14293
  node._cerebrumHabitProposalInFlight = false
@@ -14430,7 +14430,7 @@ module.exports = function (RED) {
14430
14430
  } catch (error) {
14431
14431
  node._homeMemory = updateKnxAiReconciler(node._homeMemory, { lastError: error.message || String(error) })
14432
14432
  scheduleHomeMemoryPersist()
14433
- try { node.sysLogger?.warn(`KNX AI Cerebrum state tick error: ${error.message || error}`) } catch (logError) { /* ignore */ }
14433
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Cerebrum state tick error: ${error.message || error}`) } catch (logError) { /* ignore */ }
14434
14434
  } finally {
14435
14435
  node._cerebrumStateTickInFlight = false
14436
14436
  }
@@ -14886,7 +14886,7 @@ module.exports = function (RED) {
14886
14886
  if (language) node._homeMemory.ownerLanguage = normalizeHomeLanguage(language)
14887
14887
  scheduleHomeMemoryPersist()
14888
14888
  } catch (error) {
14889
- try { node.sysLogger?.warn(`KNX AI home owner memory error: ${error.message || error}`) } catch (logError) { /* ignore */ }
14889
+ try { node.sysLogger?.warn(`Cerebrum Ultimate home owner memory error: ${error.message || error}`) } catch (logError) { /* ignore */ }
14890
14890
  }
14891
14891
  }
14892
14892
 
@@ -15075,7 +15075,7 @@ module.exports = function (RED) {
15075
15075
  }
15076
15076
  return { notify: true, content: candidate, recheckAfterMinutes }
15077
15077
  } catch (error) {
15078
- try { node.sysLogger?.warn(`KNX AI proactive Education evaluation error: ${error.message || error}`) } catch (logError) { /* ignore */ }
15078
+ try { node.sysLogger?.warn(`Cerebrum Ultimate proactive Education evaluation error: ${error.message || error}`) } catch (logError) { /* ignore */ }
15079
15079
  return { notify: false, content: '', recheckAfterMinutes: PROACTIVE_EDUCATION_RETRY_MINUTES }
15080
15080
  }
15081
15081
  }
@@ -15192,7 +15192,7 @@ module.exports = function (RED) {
15192
15192
  if (!content || content.length > 1200 || content.startsWith('{') || content.startsWith('```')) return { notify: false, content: '' }
15193
15193
  return { notify: true, content }
15194
15194
  } catch (error) {
15195
- try { node.sysLogger?.warn(`KNX AI Cerebrum habit evaluation error: ${error.message || error}`) } catch (logError) { /* ignore */ }
15195
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Cerebrum habit evaluation error: ${error.message || error}`) } catch (logError) { /* ignore */ }
15196
15196
  return { notify: false, content: '' }
15197
15197
  }
15198
15198
  }
@@ -15282,7 +15282,7 @@ module.exports = function (RED) {
15282
15282
  if (sent === true) node._proactiveGlobalSentAt.push(now)
15283
15283
  })
15284
15284
  .catch(error => {
15285
- try { node.sysLogger?.warn(`KNX AI Cerebrum habit suggestion error: ${error.message || error}`) } catch (logError) { /* ignore */ }
15285
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Cerebrum habit suggestion error: ${error.message || error}`) } catch (logError) { /* ignore */ }
15286
15286
  })
15287
15287
  .finally(() => node._proactiveInFlight.delete(inFlightKey))
15288
15288
  return
@@ -15302,7 +15302,7 @@ module.exports = function (RED) {
15302
15302
  })
15303
15303
  .catch(error => {
15304
15304
  candidate.nextCheckAt = now + (PROACTIVE_EDUCATION_RETRY_MINUTES * 60 * 1000)
15305
- try { node.sysLogger?.warn(`KNX AI proactive notification error: ${error.message || error}`) } catch (logError) { /* ignore */ }
15305
+ try { node.sysLogger?.warn(`Cerebrum Ultimate proactive notification error: ${error.message || error}`) } catch (logError) { /* ignore */ }
15306
15306
  })
15307
15307
  .finally(() => {
15308
15308
  node._proactiveInFlight.delete(candidate.ga)
@@ -16128,7 +16128,7 @@ module.exports = function (RED) {
16128
16128
  const completion = completeKnxAiScheduleRun({ store: node._scheduleStore, taskId: task.id, ok: false, error: error.message || String(error) })
16129
16129
  node._scheduleStore = completion.store
16130
16130
  scheduleScheduleStorePersist({ immediate: true })
16131
- try { node.sysLogger?.warn(`KNX AI scheduled task error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16131
+ try { node.sysLogger?.warn(`Cerebrum Ultimate scheduled task error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16132
16132
  } finally {
16133
16133
  releaseScheduledTaskIfNoPendingCamera(task.id)
16134
16134
  node._scheduleTickInFlight = false
@@ -16345,7 +16345,7 @@ module.exports = function (RED) {
16345
16345
  return await Promise.race([
16346
16346
  capturePromise,
16347
16347
  new Promise((resolve, reject) => {
16348
- timeout = setTimeout(() => reject(new Error('The KNX AI sidebar request did not produce a reply')), 25000)
16348
+ timeout = setTimeout(() => reject(new Error('The Cerebrum Ultimate sidebar request did not produce a reply')), 25000)
16349
16349
  })
16350
16350
  ])
16351
16351
  } finally {
@@ -16393,20 +16393,20 @@ module.exports = function (RED) {
16393
16393
  zh: '我无法处理这条语音消息。请重试或改为发送文字请求。'
16394
16394
  }
16395
16395
  const providerCopies = {
16396
- en: 'Telegram voice requires the OpenAI-compatible chat provider. Select it in KNX AI → AI Assistant Connection and deploy.',
16397
- it: 'I vocali Telegram richiedono il provider chat OpenAI-compatible. Selezionalo in KNX AI → Connessione Assistente AI e fai Deploy.',
16398
- de: 'Telegram-Sprachnachrichten erfordern den OpenAI-kompatiblen Chat-Provider. Wählen Sie ihn unter KNX AI → Verbindung zum KI-Assistenten aus und führen Sie Deploy aus.',
16399
- fr: 'Les messages vocaux Telegram nécessitent le fournisseur de chat OpenAI-compatible. Sélectionnez-le dans KNX AI → Connexion de l’assistant IA, puis déployez.',
16400
- es: 'Los mensajes de voz de Telegram requieren el proveedor de chat OpenAI-compatible. Selecciónalo en KNX AI → Conexión del Asistente IA y vuelve a desplegar.',
16401
- zh: 'Telegram 语音消息需要 OpenAI-compatible 聊天提供商。请在 KNX AI → AI 助手连接中选择它,然后重新部署。'
16396
+ en: 'Telegram voice requires the OpenAI-compatible chat provider. Select it in Cerebrum Ultimate → AI Assistant Connection and deploy.',
16397
+ it: 'I vocali Telegram richiedono il provider chat OpenAI-compatible. Selezionalo in Cerebrum Ultimate → Connessione Assistente AI e fai Deploy.',
16398
+ de: 'Telegram-Sprachnachrichten erfordern den OpenAI-kompatiblen Chat-Provider. Wählen Sie ihn unter Cerebrum Ultimate → Verbindung zum KI-Assistenten aus und führen Sie Deploy aus.',
16399
+ fr: 'Les messages vocaux Telegram nécessitent le fournisseur de chat OpenAI-compatible. Sélectionnez-le dans Cerebrum Ultimate → Connexion de l’assistant IA, puis déployez.',
16400
+ es: 'Los mensajes de voz de Telegram requieren el proveedor de chat OpenAI-compatible. Selecciónalo en Cerebrum Ultimate → Conexión del Asistente IA y vuelve a desplegar.',
16401
+ zh: 'Telegram 语音消息需要 OpenAI-compatible 聊天提供商。请在 Cerebrum Ultimate → AI 助手连接中选择它,然后重新部署。'
16402
16402
  }
16403
16403
  const apiKeyCopies = {
16404
- en: 'Telegram voice needs the API key of the selected OpenAI-compatible provider. Enter it in KNX AI → AI Assistant Connection and deploy.',
16405
- it: 'I vocali Telegram richiedono la API key del provider OpenAI-compatible selezionato. Inseriscila in KNX AI → Connessione Assistente AI e fai Deploy.',
16406
- de: 'Telegram-Sprachnachrichten benötigen den API-Schlüssel des ausgewählten OpenAI-kompatiblen Providers. Tragen Sie ihn unter KNX AI → Verbindung zum KI-Assistenten ein und führen Sie Deploy aus.',
16407
- fr: 'Les messages vocaux Telegram nécessitent la clé API du fournisseur OpenAI-compatible sélectionné. Saisissez-la dans KNX AI → Connexion de l’assistant IA, puis déployez.',
16408
- es: 'Los mensajes de voz de Telegram necesitan la clave API del proveedor OpenAI-compatible seleccionado. Introdúcela en KNX AI → Conexión del Asistente IA y vuelve a desplegar.',
16409
- zh: 'Telegram 语音消息需要所选 OpenAI-compatible 提供商的 API 密钥。请在 KNX AI → AI 助手连接中填写,然后重新部署。'
16404
+ en: 'Telegram voice needs the API key of the selected OpenAI-compatible provider. Enter it in Cerebrum Ultimate → AI Assistant Connection and deploy.',
16405
+ it: 'I vocali Telegram richiedono la API key del provider OpenAI-compatible selezionato. Inseriscila in Cerebrum Ultimate → Connessione Assistente AI e fai Deploy.',
16406
+ de: 'Telegram-Sprachnachrichten benötigen den API-Schlüssel des ausgewählten OpenAI-kompatiblen Providers. Tragen Sie ihn unter Cerebrum Ultimate → Verbindung zum KI-Assistenten ein und führen Sie Deploy aus.',
16407
+ fr: 'Les messages vocaux Telegram nécessitent la clé API du fournisseur OpenAI-compatible sélectionné. Saisissez-la dans Cerebrum Ultimate → Connexion de l’assistant IA, puis déployez.',
16408
+ es: 'Los mensajes de voz de Telegram necesitan la clave API del proveedor OpenAI-compatible seleccionado. Introdúcela en Cerebrum Ultimate → Conexión del Asistente IA y vuelve a desplegar.',
16409
+ zh: 'Telegram 语音消息需要所选 OpenAI-compatible 提供商的 API 密钥。请在 Cerebrum Ultimate → AI 助手连接中填写,然后重新部署。'
16410
16410
  }
16411
16411
  const copySet = error && error.code === 'KNX_AI_VOICE_PROVIDER_REQUIRED'
16412
16412
  ? providerCopies
@@ -16425,7 +16425,7 @@ module.exports = function (RED) {
16425
16425
  error: error.message || String(error)
16426
16426
  }
16427
16427
  })
16428
- try { node.sysLogger?.warn(`KNX AI Telegram voice input error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16428
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Telegram voice input error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16429
16429
  try { node.error(error, adaptedMessage) } catch (reportError) { /* ignore */ }
16430
16430
  updateConversationStatus({ type: 'request', question: content, language })
16431
16431
  sendKnxAiOutputs([null, null, replyMessage, null], adaptedMessage)
@@ -16508,7 +16508,7 @@ module.exports = function (RED) {
16508
16508
  node._sidebarAskCaptures.forEach(capture => {
16509
16509
  try {
16510
16510
  if (capture && typeof capture.resolve === 'function') {
16511
- capture.resolve({ answer: 'KNX AI node closed before the request completed.', provider: '', model: '', metadata: { type: 'node_closed' } })
16511
+ capture.resolve({ answer: 'Cerebrum Ultimate node closed before the request completed.', provider: '', model: '', metadata: { type: 'node_closed' } })
16512
16512
  }
16513
16513
  } catch (error) { /* ignore */ }
16514
16514
  })
@@ -16571,43 +16571,43 @@ module.exports = function (RED) {
16571
16571
  loadChatContextFromDisk()
16572
16572
  loadScheduleStoreFromDisk()
16573
16573
  } catch (error) {
16574
- node.sysLogger?.warn(`KNX AI history startup error: ${error.message || error}`)
16574
+ node.sysLogger?.warn(`Cerebrum Ultimate history startup error: ${error.message || error}`)
16575
16575
  }
16576
16576
 
16577
16577
  try {
16578
16578
  const cameraRegistry = getKnxAiCameraAdapterRegistry()
16579
16579
  node._cameraRegistryUnsubscribe = cameraRegistry.subscribe(() => {
16580
16580
  Promise.resolve(syncCameraAdapterRegistry({ force: true })).catch(error => {
16581
- try { node.sysLogger?.warn(`KNX AI camera adapter refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16581
+ try { node.sysLogger?.warn(`Cerebrum Ultimate camera adapter refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16582
16582
  })
16583
16583
  })
16584
16584
  Promise.resolve(syncCameraAdapterRegistry({ force: true })).catch(error => {
16585
- try { node.sysLogger?.warn(`KNX AI camera adapter startup error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16585
+ try { node.sysLogger?.warn(`Cerebrum Ultimate camera adapter startup error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16586
16586
  })
16587
16587
  node._cameraRegistrySyncTimer = setInterval(() => {
16588
16588
  Promise.resolve(syncCameraAdapterRegistry()).catch(error => {
16589
- try { node.sysLogger?.warn(`KNX AI camera adapter refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16589
+ try { node.sysLogger?.warn(`Cerebrum Ultimate camera adapter refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16590
16590
  })
16591
16591
  }, 30 * 1000)
16592
16592
  } catch (error) {
16593
- try { node.sysLogger?.warn(`KNX AI camera registry unavailable: ${error.message || error}`) } catch (logError) { /* ignore */ }
16593
+ try { node.sysLogger?.warn(`Cerebrum Ultimate camera registry unavailable: ${error.message || error}`) } catch (logError) { /* ignore */ }
16594
16594
  }
16595
16595
 
16596
16596
  try {
16597
16597
  const homeAutomationRegistry = getKnxAiHomeAutomationRegistry()
16598
16598
  node._homeAutomationRegistryUnsubscribe = homeAutomationRegistry.subscribe(() => {
16599
16599
  try { syncHomeAutomationAdapterRegistry() } catch (error) {
16600
- try { node.sysLogger?.warn(`KNX AI home automation adapter refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16600
+ try { node.sysLogger?.warn(`Cerebrum Ultimate home automation adapter refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16601
16601
  }
16602
16602
  })
16603
16603
  syncHomeAutomationAdapterRegistry()
16604
16604
  node._homeAutomationRegistrySyncTimer = setInterval(() => {
16605
16605
  try { syncHomeAutomationAdapterRegistry() } catch (error) {
16606
- try { node.sysLogger?.warn(`KNX AI home automation adapter refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16606
+ try { node.sysLogger?.warn(`Cerebrum Ultimate home automation adapter refresh error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16607
16607
  }
16608
16608
  }, 30 * 1000)
16609
16609
  } catch (error) {
16610
- try { node.sysLogger?.warn(`KNX AI home automation registry unavailable: ${error.message || error}`) } catch (logError) { /* ignore */ }
16610
+ try { node.sysLogger?.warn(`Cerebrum Ultimate home automation registry unavailable: ${error.message || error}`) } catch (logError) { /* ignore */ }
16611
16611
  }
16612
16612
 
16613
16613
  if (node._homeMemoryPeriodicTimer) clearInterval(node._homeMemoryPeriodicTimer)
@@ -16620,24 +16620,24 @@ module.exports = function (RED) {
16620
16620
  node._bootAssistantTimer = null
16621
16621
  if (node._closing === true) return
16622
16622
  Promise.resolve(emitKnxAiBootNotification()).catch(error => {
16623
- try { node.sysLogger?.warn(`KNX AI startup notification error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16623
+ try { node.sysLogger?.warn(`Cerebrum Ultimate startup notification error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16624
16624
  })
16625
16625
  }, 2500)
16626
16626
 
16627
16627
  if (node._cerebrumStateTimer) clearInterval(node._cerebrumStateTimer)
16628
16628
  Promise.resolve(runCerebrumStateTick()).catch(error => {
16629
- try { node.sysLogger?.warn(`KNX AI Cerebrum startup tick error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16629
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Cerebrum startup tick error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16630
16630
  })
16631
16631
  node._cerebrumStateTimer = setInterval(() => {
16632
16632
  Promise.resolve(runCerebrumStateTick()).catch(error => {
16633
- try { node.sysLogger?.warn(`KNX AI Cerebrum tick error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16633
+ try { node.sysLogger?.warn(`Cerebrum Ultimate Cerebrum tick error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16634
16634
  })
16635
16635
  }, CEREBRUM_STATE_TICK_MS)
16636
16636
 
16637
16637
  if (node._proactiveCheckTimer) clearInterval(node._proactiveCheckTimer)
16638
16638
  node._proactiveCheckTimer = setInterval(() => {
16639
16639
  try { checkProactiveHomeState() } catch (error) {
16640
- try { node.sysLogger?.warn(`KNX AI proactive check error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16640
+ try { node.sysLogger?.warn(`Cerebrum Ultimate proactive check error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16641
16641
  }
16642
16642
  }, 30 * 1000)
16643
16643
 
@@ -16647,11 +16647,11 @@ module.exports = function (RED) {
16647
16647
  node._scheduleStartupTimer = null
16648
16648
  if (node._closing === true) return
16649
16649
  Promise.resolve(runScheduledTaskTick()).catch(error => {
16650
- try { node.sysLogger?.warn(`KNX AI schedule startup error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16650
+ try { node.sysLogger?.warn(`Cerebrum Ultimate schedule startup error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16651
16651
  })
16652
16652
  node._scheduleTickTimer = setInterval(() => {
16653
16653
  Promise.resolve(runScheduledTaskTick()).catch(error => {
16654
- try { node.sysLogger?.warn(`KNX AI schedule tick error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16654
+ try { node.sysLogger?.warn(`Cerebrum Ultimate schedule tick error: ${error.message || error}`) } catch (logError) { /* ignore */ }
16655
16655
  })
16656
16656
  }, 15 * 1000)
16657
16657
  }, 2 * 1000)