node-red-contrib-knx-ultimate 6.1.1 → 6.2.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.
@@ -2,12 +2,14 @@
2
2
  "knxUltimateAI": {
3
3
  "title": "KNX AI (Analisi Traffico)",
4
4
  "sections": {
5
+ "quickSetup": "Configurazione rapida",
5
6
  "capture": "Cattura",
6
7
  "storage": "Storico e Summary",
7
8
  "detection": "Rilevamento e Avvisi",
8
9
  "llmConnection": "Connessione Assistente AI",
9
10
  "llmContext": "Contesto Assistente AI",
10
- "advanced": "Tuning Avanzato"
11
+ "chatAdapter": "Adattatori chat",
12
+ "advanced": "AI avanzata"
11
13
  },
12
14
  "properties": {
13
15
  "server": "Gateway",
@@ -39,19 +41,28 @@
39
41
  "llmSystemPrompt": "Prompt di sistema",
40
42
  "llmIncludeRaw": "Includi payload raw in hex",
41
43
  "llmIncludeFlowContext": "Includi inventario del progetto Node-RED",
44
+ "llmAllowKnxCommands": "Consenti all'AI di leggere stati KNX e comandare attuatori",
45
+ "llmRequireCommandConfirmation": "Chiedi conferma prima di inviare comandi KNX",
46
+ "chatAdapterPreset": "Preset adattatore",
47
+ "chatInputCode": "Mappatura ingresso (chat → KNX AI)",
48
+ "chatOutputCode": "Mappatura uscita (KNX AI → chat)",
42
49
  "llmIncludeDocsSnippets": "Includi estratti documentazione (help/README/esempi)",
43
50
  "llmDocsLanguage": "Lingua documentazione"
44
51
  },
45
52
  "outputs": {
46
53
  "summary": "Summary/Statistiche",
47
54
  "anomalies": "Anomalie",
48
- "assistant": "Assistente AI"
55
+ "assistant": "Assistente AI",
56
+ "knxCommands": "Operazioni KNX"
49
57
  },
50
58
  "selectlists": {
51
59
  "llmProvider": {
52
60
  "openai_compat": "Compatibile OpenAI (chat/completions)",
53
61
  "anthropic": "Anthropic (Claude)",
54
62
  "ollama": "Ollama (locale, beta)"
63
+ },
64
+ "chatAdapter": {
65
+ "none": "Nessun adattatore"
55
66
  }
56
67
  },
57
68
  "buttons": {
@@ -69,7 +80,9 @@
69
80
  "installedOllamaModel": "Modello Ollama installato",
70
81
  "installOllamaModelFailed": "Installazione modello Ollama non riuscita",
71
82
  "ollamaInstallSteps": "1) Apri la libreria, scegli un modello e copiane il nome (es. llama3.1). 2) Inserisci il nome nel campo Modello e clicca Installalo.",
72
- "ollamaStartedAuto": "Server Ollama avviato automaticamente."
83
+ "ollamaStartedAuto": "Server Ollama avviato automaticamente.",
84
+ "chatAdapterIntro": "Scegli un preset per inserire il codice di mappatura in ingresso e in uscita. La lista viene caricata dal file degli adattatori chat incluso nel pacchetto; il codice generato resta modificabile.",
85
+ "chatAdapterCodeHelp": "Le mappature sono sincrone. Restituisci msg per continuare oppure nessun valore per scartarlo. Gli errori vengono intercettati e segnalati senza arrestare Node-RED."
73
86
  },
74
87
  "placeholder": {
75
88
  "llmBaseUrl": "https://api.openai.com/v1/chat/completions (o endpoint compatibile)",
@@ -1,18 +1,48 @@
1
1
  <script type="text/markdown" data-help-name="knxUltimateAI">
2
2
  此节点会监听所选 KNX Ultimate 网关上的**所有 KNX 电报**,生成流量统计、检测异常,并可选调用 LLM。
3
3
 
4
+ 编辑器各部分使用与 Matter 节点相同的左侧垂直选项卡。**快速配置**仅包含常用 AI 选项(启用、提供商、凭据、模型、KNX 状态读取/执行器控制和确认);技术参数按主题归入其他选项卡。
5
+
4
6
  ## 输出
5
7
  1. **摘要/统计**(`msg.payload` 为 JSON)
6
8
  2. **异常**(`msg.payload` 为 JSON)
7
9
  3. **AI 助手**(`msg.payload` 为文本,包含 `msg.summary`)
10
+ 4. **KNX 操作**(每个通过验证的读取或写入输出一条 Universal Mode 消息)
11
+
12
+ 输出 3 和输出 4 发出的每条消息还会在 `msg.inputMessage` 中包含原始输入消息的副本。因此,原始 payload、topic、聊天元数据及其他输入属性都可供后续节点使用。克隆或输出错误会被捕获并报告,不会传播到 Node-RED 运行时。
8
13
 
9
14
  ## 命令(输入)
10
15
  发送 `msg.topic`:
11
16
  - `summary`(或空):立即输出摘要
12
17
  - `reset`:清空内部历史与计数器
13
18
  - `ask`:向已配置的 LLM 提问
19
+ - `confirm` / `cancel`:无需再次调用 LLM,即可确认或取消待处理的 KNX 命令
20
+ - `clear_chat`:清除当前会话的对话记忆
21
+
22
+ `ask` 的问题建议放在 `msg.prompt`,也可放在 `msg.payload`(字符串)或常见 Telegram 字段 `msg.payload.content` / `msg.payload.text`。
23
+
24
+ 启用 KNX 控制后,最近的对话会按 `msg.knxAi.sessionId`、`msg.sessionId` 或检测到的 Telegram 聊天 ID 保存在 RAM 中。将输出 3 连接到聊天发送节点,将输出 4 连接到配置为**通用模式**的 KNX Ultimate 节点。启用确认后,第一条回复会显示 GA、DPT 和 payload,但不会发送写入;同一会话必须在 5 分钟内回复“确认”或“取消”。新请求会替换旧的待处理计划。每条已确认命令都包含 `msg.destination`、`msg.dpt`、`msg.payload` 和 `msg.event = "GroupValue_Write"`。
25
+ 对于 DPT 1.xxx 写入,AI 生成的安全等价值 `true`/`false`、`1`/`0` 和 `on`/`off` 会在本地校验和输出前统一转换为真正的布尔值。
26
+
27
+ ### 最新 KNX 读取
28
+ 当用户明确要求当前或最新状态时,AI 可以查询已导入 ETS 目录中的精确对象,包括状态对象和其他只读对象。输出 4 会发送 `msg.destination`、`msg.dpt`、`msg.event = "GroupValue_Read"` 和 `msg.readstatus = true`。节点会为每个 `GroupValue_Response` 或最新写入等待最多 6 秒,然后在输出 3 返回解码值,并在 `msg.knxAi.readResults` 中提供详细信息。读取从不需要确认,也绝不会转换为写入。
29
+
30
+ ### 用于聊天按钮的确认请求
31
+ 计划等待确认时,输出 3 包含 `msg.knxAi.confirmationRequest`。该对象包括 `required`、`status`、`sessionId`、`expiresAt`、`commandCount`,以及 `actions` 中的两个项目。使用 `action.label` 作为 Telegram 按钮文本,使用 `action.callbackData` 作为回调,并将 `action.message` 发送回 KNX AI,即可在无需输入文本的情况下确认或取消。
32
+
33
+ ### 聊天适配器预设
34
+ **聊天适配器**选项卡从 `resources/KNXAIChatAdapterMappings.js` 加载可选映射。选择预设会在全宽文本框中插入两段可编辑的同步 JavaScript 映射:一段在 KNX AI 处理输入前运行,另一段在输出 3 发出消息前运行。返回 `msg` 以继续,或不返回值以丢弃消息。语法和执行错误会被捕获并报告,不会停止 Node-RED。
35
+
36
+ 随附的 **windkh/node-red-contrib-telegrambot** 预设遵循该包的 receiver/sender 消息约定。把 `telegram receiver` 直接连接到 KNX AI,并把输出 3 直接连接到 `telegram sender`。如需内联确认按钮,还要把配置为 `callback_query` 的 `telegram event` 连接到同一个 KNX AI 输入。输入映射会提取 `msg.payload.content`、`msg.payload.chatId` 和 Telegram 语言;输出映射会创建所需的 `msg.payload.chatId`、`type` 和 `content`,并在写入等待确认时从 `msg.knxAi.confirmationRequest` 添加 `options.reply_markup`。Telegram 包仍是独立的可选依赖项。
14
37
 
15
- `ask` 的问题建议放在 `msg.prompt`,也可放在 `msg.payload`(字符串)。
38
+ ## 快速工作流程:KNX 控制
39
+ 1. 将 ETS CSV 导入网关,并配置 LLM 提供商、模型和凭据。
40
+ 2. 启用 **LLM 助手**和 **读取 KNX 状态并控制执行器**;保持确认选项启用。
41
+ 3. 将聊天输入连接到 KNX AI,并保留稳定的会话/聊天 ID。
42
+ 4. 将输出 3 连接到聊天回复,将输出 4 连接到处于**通用模式**的 KNX Ultimate。
43
+ 5. 用户发送请求;当前状态会立即读取,而写入会先显示 GA、DPT 和值,不会立即写入总线。
44
+ 6. 同一聊天必须在 5 分钟内准确回复“确认”或“取消”。
45
+ 7. 只有“确认”会重新验证并在输出 4 发送命令;请通过 KNX 状态 GA 验证执行结果。
16
46
 
17
47
  ## 配置字段
18
48
  以下是编辑器里用户可见的全部字段名称。
@@ -53,7 +83,13 @@
53
83
  - **Endpoint URL**:chat/completions 接口 URL。
54
84
  - **API key**:API Key(本地 Ollama 可不填)。
55
85
  - **Model**:模型 ID/名称。
86
+ - **聊天模型兼容性**:所选模型必须支持已配置的 Chat Completions 端点。刷新模型列表时,会排除仅支持旧版 completions 的模型,例如 `gpt-3.5-turbo-instruct`。如果提供商拒绝自定义 temperature 值或令牌限制参数,KNX AI 会仅移除或替换不兼容字段后重试。
56
87
  - **System prompt**:KNX 分析全局系统提示词(Advanced)。
88
+ - **允许 AI 读取 KNX 状态并控制执行器**:启用输出 4,默认关闭。可以读取 ETS 目录中的精确对象;仅接受写入明确标记为 `command` 的对象。未知、DPT 不匹配、无效或数量过多的操作,以及向状态或中性对象的写入,都会在本地被拒绝。
89
+ - **发送 KNX 命令前请求确认**:默认启用。先显示已验证的修改,在同一聊天会话确认前不会发送任何 KNX 命令。有命令等待确认时,回复始终会使用当前请求的语言附加准确的确认或取消说明。命令会在输出前再次验证。
90
+ - **适配器预设**:从随附的聊天适配器文件加载一对输入/输出映射。选择预设会有意替换两个文本框,之后仍可编辑代码。
91
+ - **输入映射(聊天 → KNX AI)**:在处理输入命令前运行的同步 JavaScript。
92
+ - **输出映射(KNX AI → 聊天)**:仅应用于输出 3 消息的同步 JavaScript。
57
93
  - 如果启用了磁盘归档,**Ask** 默认会查询该归档:若问题里写了明确日期/时间范围就按其查询,否则默认查询最近 24 小时并补上当前 RAM 事件。
58
94
  - **Include raw payload hex**:在提示词中包含原始十六进制 payload。
59
95
  - **包含 Node-RED 项目清单**:在提示词中加入整个 Node-RED 项目的节点清单,不仅包含 KNX 节点,也包含 function/change/inject/template 等在内且带有 KNX 逻辑或组地址的有用节点。
@@ -84,5 +120,5 @@
84
120
  - 若 Node-RED 运行在 Docker 中,endpoint 请使用 `host.docker.internal` 替代 `localhost`。
85
121
 
86
122
  ## 安全说明
87
- 启用 LLM 后,KNX 流量上下文可能发送到所配置的 endpoint。若需严格本地化,请使用本地 provider。
123
+ 启用 LLM 后,KNX 流量上下文可能发送到所配置的 endpoint。若需严格本地化,请使用本地 provider。输出 4 上的命令仅表示已通过本地验证并转发到 flow,并不能证明执行器已执行;需要确认时请使用 KNX 状态 GA。
88
124
  </script>
@@ -2,12 +2,14 @@
2
2
  "knxUltimateAI": {
3
3
  "title": "KNX AI (Traffic Analyzer)",
4
4
  "sections": {
5
+ "quickSetup": "快速配置",
5
6
  "capture": "Capture",
6
7
  "storage": "历史与摘要",
7
8
  "detection": "检测与告警",
8
9
  "llmConnection": "AI 助手连接",
9
10
  "llmContext": "AI 助手上下文",
10
- "advanced": "高级调优"
11
+ "chatAdapter": "聊天适配器",
12
+ "advanced": "AI 高级设置"
11
13
  },
12
14
  "properties": {
13
15
  "server": "Gateway",
@@ -39,19 +41,28 @@
39
41
  "llmSystemPrompt": "System prompt",
40
42
  "llmIncludeRaw": "Include raw payload hex",
41
43
  "llmIncludeFlowContext": "包含 Node-RED 项目清单",
44
+ "llmAllowKnxCommands": "允许 AI 读取 KNX 状态并控制执行器",
45
+ "llmRequireCommandConfirmation": "发送 KNX 命令前请求确认",
46
+ "chatAdapterPreset": "适配器预设",
47
+ "chatInputCode": "输入映射(聊天 → KNX AI)",
48
+ "chatOutputCode": "输出映射(KNX AI → 聊天)",
42
49
  "llmIncludeDocsSnippets": "Include documentation snippets (help/README/examples)",
43
50
  "llmDocsLanguage": "Docs language"
44
51
  },
45
52
  "outputs": {
46
53
  "summary": "摘要/统计",
47
54
  "anomalies": "异常",
48
- "assistant": "AI 助手"
55
+ "assistant": "AI 助手",
56
+ "knxCommands": "KNX 操作"
49
57
  },
50
58
  "selectlists": {
51
59
  "llmProvider": {
52
60
  "openai_compat": "OpenAI-compatible (chat/completions)",
53
61
  "anthropic": "Anthropic (Claude)",
54
62
  "ollama": "Ollama (local, beta)"
63
+ },
64
+ "chatAdapter": {
65
+ "none": "无适配器"
55
66
  }
56
67
  },
57
68
  "messages": {
@@ -61,7 +72,9 @@
61
72
  "installedOllamaModel": "Ollama model installed",
62
73
  "installOllamaModelFailed": "Failed to install Ollama model",
63
74
  "ollamaInstallSteps": "1) Open the model library and copy the model name (for example llama3.1). 2) Put the name in the Model field and click Install it.",
64
- "ollamaStartedAuto": "Ollama server started automatically."
75
+ "ollamaStartedAuto": "Ollama server started automatically.",
76
+ "chatAdapterIntro": "选择预设即可插入输入和输出映射代码。列表从随包提供的聊天适配器文件加载;生成的代码仍可编辑。",
77
+ "chatAdapterCodeHelp": "映射同步运行。返回 msg 以继续,或不返回值以丢弃消息。错误会被捕获并报告,不会停止 Node-RED。"
65
78
  },
66
79
  "placeholder": {
67
80
  "llmBaseUrl": "https://api.openai.com/v1/chat/completions (or your compatible endpoint)",
@@ -1,86 +1,8 @@
1
1
  /**
2
2
  * (C) 2024 Supergiovane
3
3
  */
4
- const util = require('util')
5
4
  const logger = require('node-color-log')
6
5
 
7
- const DEBUG_BUFFER_LIMIT = 5000
8
- const debugBuffer = []
9
- let debugSequence = 0
10
-
11
- const cloneEntry = (entry) => ({
12
- seq: entry.seq,
13
- level: entry.level,
14
- prefix: entry.prefix,
15
- message: entry.message,
16
- timestamp: entry.timestamp,
17
- isoTimestamp: entry.isoTimestamp,
18
- sessionStart: entry.sessionStart
19
- })
20
-
21
- const normaliseLevel = (level) => {
22
- const allowed = ['success', 'debug', 'info', 'warn', 'error']
23
- if (allowed.includes(level)) return level
24
- return 'info'
25
- }
26
-
27
- const stringifyArg = (arg) => {
28
- if (arg === null) return 'null'
29
- if (arg === undefined) return 'undefined'
30
- if (arg instanceof Error) return arg.stack || `${arg.name || 'Error'}: ${arg.message}`
31
- const type = typeof arg
32
- if (type === 'string') return arg
33
- if (type === 'number' || type === 'boolean' || type === 'bigint') return String(arg)
34
- if (type === 'symbol') return arg.toString()
35
- try {
36
- return util.inspect(arg, { depth: 6, breakLength: 120, compact: false })
37
- } catch (error) {
38
- return String(arg)
39
- }
40
- }
41
-
42
- const formatArgs = (args) => {
43
- if (!args || !args.length) return ''
44
- return args.map(stringifyArg).join(' ')
45
- }
46
-
47
- const pushDebugEntry = (level, prefix, args, meta = {}) => {
48
- try {
49
- const effectiveLevel = normaliseLevel(level)
50
- const timestamp = Date.now()
51
- const isoTimestamp = new Date(timestamp).toISOString()
52
- const message = formatArgs(args)
53
- debugSequence += 1
54
- debugBuffer.push({
55
- seq: debugSequence,
56
- level: effectiveLevel,
57
- prefix: prefix || '',
58
- message,
59
- timestamp,
60
- isoTimestamp,
61
- sessionStart: !!meta.sessionStart
62
- })
63
- if (debugBuffer.length > DEBUG_BUFFER_LIMIT) {
64
- debugBuffer.splice(0, debugBuffer.length - DEBUG_BUFFER_LIMIT)
65
- }
66
- } catch (error) {
67
- // As a last resort, make sure the failure itself is recorded.
68
- debugSequence += 1
69
- debugBuffer.push({
70
- seq: debugSequence,
71
- level: 'error',
72
- prefix: prefix || '',
73
- message: `Logger buffer failure: ${error.message || error}`,
74
- timestamp: Date.now(),
75
- isoTimestamp: new Date().toISOString(),
76
- sessionStart: false
77
- })
78
- if (debugBuffer.length > DEBUG_BUFFER_LIMIT) {
79
- debugBuffer.splice(0, debugBuffer.length - DEBUG_BUFFER_LIMIT)
80
- }
81
- }
82
- }
83
-
84
6
  class loggerClass {
85
7
  logLevel = 'info'
86
8
  prefix = ''
@@ -99,62 +21,31 @@ class loggerClass {
99
21
  this.logger.setLevel(requestedLevel)
100
22
  this.logger.setDate(() => (new Date()).toLocaleString())
101
23
  this.logLevel = requestedLevel
102
- pushDebugEntry('info', this.prefix, [`--- Logger session started (level: ${this.logLevel}) ---`], { sessionStart: true })
103
24
  }
104
25
 
105
26
  destroy = () => {
106
27
  // Placeholder for backward compatibility; kept for API symmetry.
107
28
  }
108
29
 
109
- record = (level, args) => {
110
- pushDebugEntry(level, this.prefix, args)
111
- }
112
-
113
30
  success = (...args) => {
114
- this.record('success', args)
115
31
  this.logger.success(...args)
116
32
  }
117
33
 
118
34
  debug = (...args) => {
119
- this.record('debug', args)
120
35
  this.logger.debug(...args)
121
36
  }
122
37
 
123
38
  info = (...args) => {
124
- this.record('info', args)
125
39
  this.logger.info(...args)
126
40
  }
127
41
 
128
42
  warn = (...args) => {
129
- this.record('warn', args)
130
43
  this.logger.warn(...args)
131
44
  }
132
45
 
133
46
  error = (...args) => {
134
- this.record('error', args)
135
47
  this.logger.error(...args)
136
48
  }
137
49
  }
138
50
 
139
- loggerClass.getDebugSnapshot = (options = {}) => {
140
- const sinceSeqRaw = options.sinceSeq
141
- const sinceSeq = Number.isInteger(sinceSeqRaw) ? sinceSeqRaw : null
142
- const source = sinceSeq === null ? debugBuffer : debugBuffer.filter((entry) => entry.seq > sinceSeq)
143
- const entries = source.map(cloneEntry)
144
- const latestSeq = debugBuffer.length ? debugBuffer[debugBuffer.length - 1].seq : (sinceSeq || 0)
145
- return {
146
- entries,
147
- latestSeq,
148
- total: debugBuffer.length,
149
- limit: DEBUG_BUFFER_LIMIT
150
- }
151
- }
152
-
153
- loggerClass.clearDebugBuffer = () => {
154
- debugBuffer.length = 0
155
- debugSequence = 0
156
- }
157
-
158
- loggerClass.DEBUG_BUFFER_LIMIT = DEBUG_BUFFER_LIMIT
159
-
160
51
  module.exports = loggerClass
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=20.18.1"
5
5
  },
6
- "version": "6.1.1",
6
+ "version": "6.2.0",
7
7
  "description": "KNX Ultimate is the most advanced KNX integration for Node-RED, providing secure KNX/IP communication, routing, ETS project import, Philips Hue, Matter Controller and Matter Bridge (control matter device via KNX and expose KNX GA via Matter), MQTT, diagnostics with AI, virtual devices, and powerful automation nodes. Build professional, reliable, and scalable smart home and building automation projects with minimal effort.",
8
8
  "files": [
9
9
  "nodes/",
@@ -32,7 +32,6 @@
32
32
  "version": ">=3.1.1",
33
33
  "plugins": {
34
34
  "commonFunctions": "/nodes/commonFunctions.js",
35
- "knxUltimateMonitorSidebar": "/nodes/plugins/knxUltimateMonitor-sidebar-plugin.html",
36
35
  "knxUltimateFlowBubbles": "/nodes/plugins/knxUltimate-flow-bubbles-plugin.html"
37
36
  },
38
37
  "nodes": {
@@ -0,0 +1,87 @@
1
+ ; (function (root) {
2
+ const KNXAIChatAdapterMappings = [
3
+ {
4
+ id: 'windkh-telegrambot',
5
+ title: 'windkh/node-red-contrib-telegrambot',
6
+ inputCode: `// Receiver/Event -> KNX AI
7
+ // Supports "telegram receiver" text messages and "telegram event"
8
+ // callback_query messages produced by inline confirmation buttons.
9
+ const telegram = msg.payload;
10
+ if (!telegram || typeof telegram !== 'object') return;
11
+
12
+ const chatId = telegram.chatId;
13
+ const content = typeof telegram.content === 'string' ? telegram.content.trim() : '';
14
+ if (chatId === undefined || chatId === null || content === '') return;
15
+
16
+ msg.sessionId = String(chatId);
17
+ msg.language =
18
+ (msg.originalMessage && msg.originalMessage.from && msg.originalMessage.from.language_code) ||
19
+ msg.language ||
20
+ '';
21
+
22
+ if (telegram.type === 'callback_query' && (content === 'confirm' || content === 'cancel')) {
23
+ msg.topic = content;
24
+ msg.knxAi = Object.assign({}, msg.knxAi, {
25
+ sessionId: String(chatId),
26
+ confirm: content === 'confirm'
27
+ });
28
+ return msg;
29
+ }
30
+
31
+ if (telegram.type !== 'message') return;
32
+ msg.topic = 'ask';
33
+ msg.prompt = content;
34
+ return msg;`,
35
+ outputCode: `// KNX AI chat output -> Telegram sender
36
+ // Converts the text reply and, when required, adds inline
37
+ // confirmation/cancellation buttons.
38
+ const source = msg.inputMessage && typeof msg.inputMessage === 'object'
39
+ ? msg.inputMessage
40
+ : inputMessage;
41
+ const sourcePayload = source && source.payload && typeof source.payload === 'object'
42
+ ? source.payload
43
+ : {};
44
+ const chatId = sourcePayload.chatId !== undefined
45
+ ? sourcePayload.chatId
46
+ : source && source.chatId;
47
+ if (chatId === undefined || chatId === null) return;
48
+
49
+ let content = msg.payload;
50
+ if (content && typeof content === 'object') {
51
+ content = content.error || content.message || JSON.stringify(content);
52
+ }
53
+ content = String(content === undefined || content === null ? '' : content);
54
+
55
+ const telegramPayload = {
56
+ chatId: chatId,
57
+ type: 'message',
58
+ content: content
59
+ };
60
+ const confirmation = msg.knxAi && msg.knxAi.confirmationRequest;
61
+ if (confirmation && confirmation.required === true && Array.isArray(confirmation.actions)) {
62
+ telegramPayload.options = {
63
+ reply_markup: JSON.stringify({
64
+ inline_keyboard: [
65
+ confirmation.actions.map(function (action) {
66
+ return {
67
+ text: action.label,
68
+ callback_data: action.callbackData
69
+ };
70
+ })
71
+ ]
72
+ })
73
+ };
74
+ }
75
+
76
+ msg.payload = telegramPayload;
77
+ return msg;`
78
+ }
79
+ ]
80
+
81
+ if (root) {
82
+ root.KNXAIChatAdapterMappings = KNXAIChatAdapterMappings
83
+ }
84
+ if (typeof module !== 'undefined' && module.exports) {
85
+ module.exports = KNXAIChatAdapterMappings
86
+ }
87
+ })(typeof window !== 'undefined' ? window : (typeof globalThis !== 'undefined' ? globalThis : this))