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.
- package/CHANGELOG.md +124 -121
- package/README.md +4 -0
- package/nodes/knxUltimateAI.html +9 -5
- package/nodes/knxUltimateAI.js +143 -143
- package/nodes/knxUltimateAIHomeAssistant.html +7 -5
- package/nodes/knxUltimateAIHomeAssistant.js +1 -1
- package/nodes/knxUltimateHueController.html +1 -1
- package/nodes/knxUltimateMatterBridge.html +2 -2
- package/nodes/knxUltimateMatterControllerDevice.html +12 -12
- package/nodes/locales/de/knxUltimateAI.html +4 -199
- package/nodes/locales/de/knxUltimateAI.json +6 -6
- package/nodes/locales/de/knxUltimateViewer.html +1 -1
- package/nodes/locales/en/knxUltimateAI.html +4 -211
- package/nodes/locales/en/knxUltimateAI.json +6 -6
- package/nodes/locales/en/knxUltimateViewer.html +1 -1
- package/nodes/locales/es/knxUltimateAI.html +4 -199
- package/nodes/locales/es/knxUltimateAI.json +6 -6
- package/nodes/locales/es/knxUltimateViewer.html +1 -1
- package/nodes/locales/fr/knxUltimateAI.html +4 -199
- package/nodes/locales/fr/knxUltimateAI.json +6 -6
- package/nodes/locales/fr/knxUltimateViewer.html +1 -1
- package/nodes/locales/it/knxUltimateAI.html +4 -211
- package/nodes/locales/it/knxUltimateAI.json +6 -6
- package/nodes/locales/it/knxUltimateViewer.html +1 -1
- package/nodes/locales/zh-CN/knxUltimateAI.html +4 -199
- package/nodes/locales/zh-CN/knxUltimateAI.json +6 -6
- package/nodes/locales/zh-CN/knxUltimateViewer.html +1 -1
- package/nodes/plugins/knxUltimateAI-vue/assets/app.css +1 -1
- package/nodes/plugins/knxUltimateAI-vue/assets/app.js +10 -10
- package/nodes/plugins/knxUltimateAI-vue/index.html +1 -1
- package/nodes/plugins/knxUltimateViewer-vue/assets/app.js +2 -2
- package/nodes/utils/knxAiChatContext.js +4 -4
- package/nodes/utils/knxAiHomeMemory.js +2 -2
- package/nodes/utils/knxAiScheduler.js +2 -2
- package/package.json +2 -2
- package/resources/KNXAIChatAdapterMappings.js +4 -4
- package/examples/KNX AI - Conversational Control with Confirmation.json +0 -395
- package/examples/KNX AI - Summary Anomalies and Ask.json +0 -226
- package/examples/KNX AI - Telegrambot Direct Chat.json +0 -198
package/nodes/knxUltimateAI.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 区域/组。` : '
|
|
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: '我的
|
|
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}` : ''}!尚未为
|
|
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
|
-
//
|
|
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: '
|
|
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('
|
|
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: '
|
|
2007
|
-
it: '
|
|
2008
|
-
de: '
|
|
2009
|
-
fr: '
|
|
2010
|
-
es: '
|
|
2011
|
-
zh: '
|
|
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('
|
|
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
|
|
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(`
|
|
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(`
|
|
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
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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 || '
|
|
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 || '
|
|
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
|
-
'
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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
|
|
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
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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
|
|
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
|
|
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(`
|
|
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
|
|
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(`
|
|
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
|
|
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
|
|
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
|
|
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(`
|
|
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
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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
|
|
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
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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
|
|
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
|
|
16397
|
-
it: 'I vocali Telegram richiedono il provider chat OpenAI-compatible. Selezionalo in
|
|
16398
|
-
de: 'Telegram-Sprachnachrichten erfordern den OpenAI-kompatiblen Chat-Provider. Wählen Sie ihn unter
|
|
16399
|
-
fr: 'Les messages vocaux Telegram nécessitent le fournisseur de chat OpenAI-compatible. Sélectionnez-le dans
|
|
16400
|
-
es: 'Los mensajes de voz de Telegram requieren el proveedor de chat OpenAI-compatible. Selecciónalo en
|
|
16401
|
-
zh: 'Telegram 语音消息需要 OpenAI-compatible 聊天提供商。请在
|
|
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
|
|
16405
|
-
it: 'I vocali Telegram richiedono la API key del provider OpenAI-compatible selezionato. Inseriscila in
|
|
16406
|
-
de: 'Telegram-Sprachnachrichten benötigen den API-Schlüssel des ausgewählten OpenAI-kompatiblen Providers. Tragen Sie ihn unter
|
|
16407
|
-
fr: 'Les messages vocaux Telegram nécessitent la clé API du fournisseur OpenAI-compatible sélectionné. Saisissez-la dans
|
|
16408
|
-
es: 'Los mensajes de voz de Telegram necesitan la clave API del proveedor OpenAI-compatible seleccionado. Introdúcela en
|
|
16409
|
-
zh: 'Telegram 语音消息需要所选 OpenAI-compatible 提供商的 API 密钥。请在
|
|
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(`
|
|
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: '
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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)
|