node-red-contrib-knx-ultimate 5.0.4 → 5.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/nodes/commonFunctions.js +93 -0
  3. package/nodes/icons/node-matter-icon.svg +12 -0
  4. package/nodes/knxUltimateMatterBridge.html +342 -0
  5. package/nodes/knxUltimateMatterBridge.js +313 -0
  6. package/nodes/knxUltimateMatterDevice.html +632 -0
  7. package/nodes/knxUltimateMatterDevice.js +308 -0
  8. package/nodes/locales/de/knxUltimateMatterBridge.html +53 -0
  9. package/nodes/locales/de/knxUltimateMatterBridge.json +83 -0
  10. package/nodes/locales/de/knxUltimateMatterDevice.html +56 -0
  11. package/nodes/locales/de/knxUltimateMatterDevice.json +109 -0
  12. package/nodes/locales/de/matter-config.html +25 -0
  13. package/nodes/locales/de/matter-config.json +26 -0
  14. package/nodes/locales/de/matterbridge-config.html +34 -0
  15. package/nodes/locales/de/matterbridge-config.json +20 -0
  16. package/nodes/locales/en/knxUltimateMatterBridge.html +53 -0
  17. package/nodes/locales/en/knxUltimateMatterBridge.json +83 -0
  18. package/nodes/locales/en/knxUltimateMatterDevice.html +56 -0
  19. package/nodes/locales/en/knxUltimateMatterDevice.json +109 -0
  20. package/nodes/locales/en/matter-config.html +25 -0
  21. package/nodes/locales/en/matter-config.json +26 -0
  22. package/nodes/locales/en/matterbridge-config.html +34 -0
  23. package/nodes/locales/en/matterbridge-config.json +20 -0
  24. package/nodes/locales/es/knxUltimateMatterBridge.html +53 -0
  25. package/nodes/locales/es/knxUltimateMatterBridge.json +83 -0
  26. package/nodes/locales/es/knxUltimateMatterDevice.html +56 -0
  27. package/nodes/locales/es/knxUltimateMatterDevice.json +109 -0
  28. package/nodes/locales/es/matter-config.html +25 -0
  29. package/nodes/locales/es/matter-config.json +26 -0
  30. package/nodes/locales/es/matterbridge-config.html +34 -0
  31. package/nodes/locales/es/matterbridge-config.json +20 -0
  32. package/nodes/locales/fr/knxUltimateMatterBridge.html +53 -0
  33. package/nodes/locales/fr/knxUltimateMatterBridge.json +83 -0
  34. package/nodes/locales/fr/knxUltimateMatterDevice.html +56 -0
  35. package/nodes/locales/fr/knxUltimateMatterDevice.json +109 -0
  36. package/nodes/locales/fr/matter-config.html +25 -0
  37. package/nodes/locales/fr/matter-config.json +26 -0
  38. package/nodes/locales/fr/matterbridge-config.html +34 -0
  39. package/nodes/locales/fr/matterbridge-config.json +20 -0
  40. package/nodes/locales/it/knxUltimateMatterBridge.html +53 -0
  41. package/nodes/locales/it/knxUltimateMatterBridge.json +83 -0
  42. package/nodes/locales/it/knxUltimateMatterDevice.html +56 -0
  43. package/nodes/locales/it/knxUltimateMatterDevice.json +109 -0
  44. package/nodes/locales/it/matter-config.html +25 -0
  45. package/nodes/locales/it/matter-config.json +26 -0
  46. package/nodes/locales/it/matterbridge-config.html +34 -0
  47. package/nodes/locales/it/matterbridge-config.json +20 -0
  48. package/nodes/locales/zh-CN/knxUltimateMatterBridge.html +53 -0
  49. package/nodes/locales/zh-CN/knxUltimateMatterBridge.json +83 -0
  50. package/nodes/locales/zh-CN/knxUltimateMatterDevice.html +56 -0
  51. package/nodes/locales/zh-CN/knxUltimateMatterDevice.json +109 -0
  52. package/nodes/locales/zh-CN/matter-config.html +25 -0
  53. package/nodes/locales/zh-CN/matter-config.json +26 -0
  54. package/nodes/locales/zh-CN/matterbridge-config.html +34 -0
  55. package/nodes/locales/zh-CN/matterbridge-config.json +20 -0
  56. package/nodes/matter-config.html +226 -0
  57. package/nodes/matter-config.js +242 -0
  58. package/nodes/matterbridge-config.html +121 -0
  59. package/nodes/matterbridge-config.js +220 -0
  60. package/nodes/utils/matterBridgeDeviceFactory.mjs +511 -0
  61. package/nodes/utils/matterBridgeEngine.mjs +290 -0
  62. package/nodes/utils/matterEngine.mjs +475 -0
  63. package/nodes/utils/matterKnxConverter.js +204 -0
  64. package/package.json +8 -2
@@ -0,0 +1,56 @@
1
+ <script type="text/markdown" data-help-name="knxUltimateMatterDevice">
2
+ # Matter 设备 (BETA)
3
+
4
+ > 此节点处于 **BETA** 阶段:可以正常工作,但细节可能在版本之间发生变化。
5
+
6
+ ## 概述
7
+
8
+ Matter Device 节点在 **Matter 设备**和 **KNX 组地址**之间架起桥梁。该节点通过 *Matter 控制器*配置节点工作,后者将设备调试进自己的 Matter fabric。
9
+
10
+ - 从 KNX 总线**控制**任何已配对的 Matter 设备(开/关、调光、窗帘、温控器、门锁等)。
11
+ - 在 KNX 组地址上**跟踪**设备的每个属性(状态反馈、传感器、功率测量、电池等)。
12
+ - 完全通用:映射列表基于设备**真实暴露的端点/集群**构建。
13
+
14
+ ## 配置
15
+
16
+ |字段|说明|
17
+ |--|--|
18
+ | KNX 网关 | 用于报文的 KNX 网关 |
19
+ | Matter 控制器 | Matter 控制器配置节点(在那里配对设备) |
20
+ | 设备 | 通过自动补全选择已配对的 Matter 设备 |
21
+ | 映射 | 每行一个组地址 ↔ Matter 集群映射 |
22
+ | 启动时读取状态 | 启用后,节点在部署/连接时发送当前缓存的值 |
23
+ | 节点输入/输出引脚 | 启用 Node-RED 引脚以进行原始 Matter 访问 |
24
+
25
+ ## 映射
26
+
27
+ 目标列表以易懂的名称显示支持的功能,例如*"开/关"*或*"瞬时功率 (W)"*,按设备实际暴露的功能过滤,并在方括号中显示当前值。
28
+
29
+ 每行映射有一个**方向**:
30
+
31
+ - **KNX → Matter(命令)**:组地址上收到的报文调用 Matter 集群命令或写入属性。示例:GA `1/1/1` DPT 1.001 → `OnOff.on/off`(布尔值自动选择开或关)。
32
+ - **Matter → KNX(状态)**:订阅的 Matter 属性变化时,其值被转换并写入组地址。`GroupValue_Read` 请求以缓存值应答。
33
+
34
+ 常见集群自动转换为适合 KNX 的值:
35
+
36
+ |集群|转换|
37
+ |--|--|
38
+ | OnOff | 布尔 (DPT 1.001) |
39
+ | LevelControl | 0-254 ↔ 百分比 (DPT 5.001) |
40
+ | WindowCovering | percent100ths ↔ 百分比 (DPT 5.001),上/下 (DPT 1.008) |
41
+ | ColorControl | mireds ↔ 开尔文 (DPT 7.600) |
42
+ | Thermostat | 厘°C ↔ °C (DPT 9.001) |
43
+ | 温度/湿度 | 厘单位 ↔ 单位 (DPT 9.001/9.007) |
44
+ | 照度 | 对数刻度 ↔ Lux (DPT 9.004) |
45
+ | PowerSource | 半百分比 ↔ 电池百分比 (DPT 5.001) |
46
+ | 电功率/电能 | mW ↔ W (DPT 14.056),mWh ↔ kWh (DPT 13.013) |
47
+
48
+ 其他集群/属性原样传递;您选择的 DPT 执行最终的 KNX 编码。
49
+
50
+ ## 节点引脚
51
+
52
+ 如果启用节点引脚:
53
+
54
+ - **输入**:发送原始命令:`msg.payload = { endpointId: 1, clusterId: 6, command: "toggle" }` 或属性写入:`msg.payload = { endpointId: 1, clusterId: 8, attribute: "onLevel", value: 128 }`
55
+ - **输出**:接收每个属性变化(`msg.matter` 包含完整事件)和每个集群事件(按钮按下等)。
56
+ </script>
@@ -0,0 +1,109 @@
1
+ {
2
+ "knxUltimateMatterDevice": {
3
+ "title": "Matter 设备 (BETA)",
4
+ "paletteLabel": "Matter Device (BETA)",
5
+ "matter_controller": "Matter 控制器",
6
+ "matter_device": "设备",
7
+ "device_placeholder": "点击选择已配对的 Matter 设备",
8
+ "mapping_help": "每个映射添加一行。\"KNX → Matter\" 在组地址收到报文时调用集群命令(或写入属性)。\"Matter → KNX\" 在属性变化时将其值写入组地址(读请求以缓存值应答)。",
9
+ "select_target": "-- 选择 Matter 目标 --",
10
+ "stored_target": "已保存",
11
+ "read_status_startup": "启动时读取状态",
12
+ "opt_yes_emit": "是,并发送 KNX 报文。",
13
+ "opt_no": "否",
14
+ "node_pins": "节点输入/输出引脚",
15
+ "node_pins_hide": "隐藏",
16
+ "node_pins_show": "显示节点输入/输出引脚",
17
+ "direction": {
18
+ "command": "KNX → Matter(命令)",
19
+ "status": "Matter → KNX(状态)"
20
+ },
21
+ "fields": {
22
+ "direction": "方向",
23
+ "target": "Matter 端点 / 集群 / 目标",
24
+ "ga": "组地址",
25
+ "dpt": "DPT",
26
+ "ganame": "名称"
27
+ },
28
+ "no_simple_targets": "此设备上未找到支持的功能(或设备尚未连接)。",
29
+ "endpoint": "端点",
30
+ "friendly": {
31
+ "onoff_switch": "开/关(打开/关闭)",
32
+ "onoff_toggle": "切换状态(toggle)",
33
+ "onoff_state": "开/关状态",
34
+ "level_set": "设置亮度 / 级别 (%)",
35
+ "level_state": "当前亮度 / 级别 (%)",
36
+ "colortemp_set": "设置色温(开尔文)",
37
+ "colortemp_state": "当前色温(开尔文)",
38
+ "cover_updown": "窗帘 上/下",
39
+ "cover_stop": "窗帘 停止",
40
+ "cover_position_set": "窗帘移动到位置 (%)",
41
+ "cover_position_state": "当前窗帘位置 (%)",
42
+ "cover_tilt_set": "叶片倾斜 (%)",
43
+ "cover_tilt_state": "当前叶片倾斜 (%)",
44
+ "thermo_localtemp": "室内温度 (°C)",
45
+ "thermo_heatsp_set": "设置采暖设定点 (°C)",
46
+ "thermo_coolsp_set": "设置制冷设定点 (°C)",
47
+ "thermo_heatsp_state": "采暖设定点 (°C)",
48
+ "thermo_coolsp_state": "制冷设定点 (°C)",
49
+ "fan_speed_set": "设置风扇速度 (%)",
50
+ "fan_speed_state": "当前风扇速度 (%)",
51
+ "lock_cmd": "上锁/解锁",
52
+ "lock_state": "锁状态(已上锁)",
53
+ "temp_measured": "测量温度 (°C)",
54
+ "humidity_measured": "相对湿度 (%)",
55
+ "illuminance_measured": "照度 (Lux)",
56
+ "occupancy_state": "检测到人员",
57
+ "contact_state": "触点状态(闭合/断开)",
58
+ "battery_percent": "电池电量 (%)",
59
+ "power_active": "瞬时功率 (W)",
60
+ "power_voltage": "电压 (V)",
61
+ "power_current": "电流 (A)",
62
+ "energy_imported": "累计耗电量 (kWh)",
63
+ "identify_cmd": "识别设备(闪烁)",
64
+ "switch_position": "按钮位置"
65
+ },
66
+ "devtype": {
67
+ "onofflight": "开关灯",
68
+ "dimmablelight": "调光灯",
69
+ "colortemperaturelight": "色温灯",
70
+ "extendedcolorlight": "彩色灯",
71
+ "onoffpluginunit": "插座(开/关)",
72
+ "dimmablepluginunit": "插座(可调光)",
73
+ "smartplug": "智能插座",
74
+ "contactsensor": "门磁传感器",
75
+ "lightsensor": "光照传感器",
76
+ "occupancysensor": "人体传感器",
77
+ "temperaturesensor": "温度传感器",
78
+ "humiditysensor": "湿度传感器",
79
+ "pressuresensor": "压力传感器",
80
+ "flowsensor": "流量传感器",
81
+ "doorlock": "门锁",
82
+ "windowcovering": "窗帘",
83
+ "thermostat": "温控器",
84
+ "fan": "风扇",
85
+ "genericswitch": "按钮/开关",
86
+ "airqualitysensor": "空气质量传感器",
87
+ "smokecoalarm": "烟雾/CO 报警器",
88
+ "waterleakdetector": "漏水探测器",
89
+ "roomairconditioner": "空调",
90
+ "airpurifier": "空气净化器",
91
+ "speaker": "音箱",
92
+ "pumpcontroller": "水泵",
93
+ "onofflightswitch": "灯开关",
94
+ "dimmerswitch": "调光开关",
95
+ "colordimmerswitch": "彩色调光开关",
96
+ "powersource": "电源",
97
+ "electricalsensor": "电量传感器",
98
+ "bridgednode": "桥接设备",
99
+ "aggregator": "桥接器/聚合器",
100
+ "rootnode": "根节点"
101
+ }
102
+ },
103
+ "common": {
104
+ "ga": "GA",
105
+ "dpt": "DPT",
106
+ "name": "名称",
107
+ "knx_gw": "KNX 网关"
108
+ }
109
+ }
@@ -0,0 +1,25 @@
1
+ <script type="text/markdown" data-help-name="matter-config">
2
+ # Matter 控制器
3
+
4
+ ## 概述
5
+
6
+ 此配置节点是一个完整的 **Matter 控制器**:它创建自己的 Matter *fabric* 并将您的 Matter 设备配对(调试)进来。配对后的设备可供 **Matter Device** 节点使用,将它们映射到 KNX 组地址。
7
+
8
+ 控制器通过 **IP 网络**(WiFi、以太网或经边界路由器的 Thread)与设备通信。不支持蓝牙配对:设备必须已经可以通过网络访问。
9
+
10
+ ## 配对设备
11
+
12
+ 1. 先**部署**此配置节点(控制器必须已运行)。
13
+ 2. 重新打开节点并输入**配对码**:11 位手动配对码(例如 `3497-011-2332`)或二维码内容(`MT:....`)。
14
+ 3. 点击**配对**。调试过程最多可能需要一分钟。
15
+
16
+ 如果设备是全新的且仅支持蓝牙配对,请先用厂商 App 或其他 Matter 控制器(Alexa、Google Home、Apple Home)配对,然后使用其**"与其他中枢共享/配对"**功能为 KNX-Ultimate 生成新的配对码。这样设备可以同时加入多个 fabric。
17
+
18
+ ## 存储
19
+
20
+ Fabric 凭据和已配对设备保存在 Node-RED 用户目录下的 `knxultimatestorage/matter` 文件夹中。删除该文件夹将移除所有配对。
21
+
22
+ ## 移除设备
23
+
24
+ 使用已配对设备列表中的垃圾桶按钮。控制器会尝试正确地解除设备调试;如果设备无法访问,仍会从 fabric 中移除(之后可能需要对设备进行恢复出厂设置)。
25
+ </script>
@@ -0,0 +1,26 @@
1
+ {
2
+ "matter-config": {
3
+ "properties": {
4
+ "title": "Matter 控制器",
5
+ "intro": "此节点是一个 Matter 控制器(拥有自己的 fabric)。在此配对您的 Matter 设备,然后使用 \"Matter Device\" 节点将其映射到 KNX 组地址。请先部署该节点,然后回到这里配对设备。",
6
+ "node-config-input-name": "名称",
7
+ "fabric_label": "Fabric 标签",
8
+ "pairing_title": "配对新设备",
9
+ "pairing_help": "输入 11 位手动配对码(例如 3497-011-2332)或二维码内容(MT:....)。设备必须可通过 IP 网络访问(WiFi/以太网或通过边界路由器的 Thread)。如果设备已与其他控制器(Alexa、Google、Apple)配对,请使用其\"与其他中枢共享/配对\"功能获取新的配对码。",
10
+ "pairing_code": "配对码",
11
+ "pair_button": "配对",
12
+ "devices_title": "已配对设备",
13
+ "col_name": "名称",
14
+ "col_product": "产品",
15
+ "col_state": "状态",
16
+ "no_devices": "尚未配对任何设备。",
17
+ "unpair_confirm": "移除(解除配对)设备",
18
+ "unpair_ok": "设备已移除。",
19
+ "deploy_first": "请先部署此配置节点,然后重试。",
20
+ "pairing_code_missing": "请输入配对码。",
21
+ "pairing_wait": "正在配对,最多可能需要一分钟...",
22
+ "pairing_failed": "配对失败",
23
+ "pairing_ok": "设备配对成功!"
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,34 @@
1
+ <script type="text/markdown" data-help-name="matterbridge-config">
2
+ # Matter 桥接器 (BETA)
3
+
4
+ > 此节点处于 **BETA** 阶段:可以正常工作,但细节可能在版本之间发生变化。
5
+
6
+ ## 概述
7
+
8
+ 此配置节点就是 **Matter 桥接器本身**:它运行 Matter 服务器,Alexa、Google Home、Apple Home(或任何 Matter 控制器)只需配对**一次**。流程中的每个 **Matter Bridge device** 节点都指向这里,并在 App 中显示为桥接器的一个设备。
9
+
10
+ ## 配置
11
+
12
+ |字段|说明|
13
+ |--|--|
14
+ | 名称 | 此配置节点在 Node-RED 中的名称 |
15
+ | Matter 桥接器名称 | 桥接器本身在 Matter App 中显示的名称 |
16
+ | 端口 | Matter 服务器的 UDP 端口(默认 5540)。每个桥接器需要独立的端口,因此可以运行**多个独立的桥接器** |
17
+
18
+ ## 配对
19
+
20
+ 1. **部署**,等待几秒,然后重新打开此节点。
21
+ 2. 配对面板显示**二维码**和**手动配对码**:在 Alexa / Google Home / Apple Home 中扫描或输入("添加 Matter 设备")。
22
+ 3. 多个控制器可以与同一个桥接器配对(Matter 多 fabric)。
23
+
24
+ **重置配对**按钮会移除所有已配对的控制器并重新开始配对广播。
25
+
26
+ ## 身份与存储
27
+
28
+ 桥接器身份与此配置节点绑定,保存在 Node-RED 用户目录的 `knxultimatestorage/matter` 中:重新部署(即使更改端口或名称)**不**需要重新配对。只有删除此配置节点并新建一个才会改变身份——此时请从 Matter App 中移除旧桥接器并重新配对。
29
+
30
+ ## 说明
31
+
32
+ - Node-RED 主机必须启用 **IPv6 link-local**(Matter 标准要求),并且控制器可在本地网络访问到它。
33
+ - 添加/重命名/移除的设备节点会在几秒内被已配对的控制器识别,无需重新配对。
34
+ </script>
@@ -0,0 +1,20 @@
1
+ {
2
+ "matterbridge-config": {
3
+ "properties": {
4
+ "title": "Matter 桥接器",
5
+ "intro": "此配置节点就是 Matter 桥接器本身:Alexa、Google Home、Apple Home(或任何 Matter 控制器)配对一次即可看到你挂接的所有 KNX 设备。在流程中添加 \"Matter Bridge device\" 节点并指向这里。先部署,然后重新打开获取配对码。",
6
+ "node-config-input-name": "名称",
7
+ "bridge_name": "Matter 桥接器名称",
8
+ "port": "端口",
9
+ "pairing_title": "配对(Alexa、Google Home、Apple Home...)",
10
+ "not_running": "Matter 桥接器尚未运行。请先部署,等待几秒后点击刷新。",
11
+ "commissioned": "桥接器已与以下控制器配对:",
12
+ "awaiting": "等待配对。请在您的 Matter App 中扫描二维码或输入手动配对码:",
13
+ "qr_link": "在浏览器中打开二维码",
14
+ "deploy_first": "请先部署此配置节点,然后点击刷新。",
15
+ "reset_button": "重置配对",
16
+ "reset_confirm": "移除所有已配对的控制器并重新开始配对?设备将从 Alexa/Google/Apple 中消失。",
17
+ "reset_ok": "配对已重置。桥接器重新进入配对模式。"
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,226 @@
1
+ <script type="text/javascript" src="resources/node-red-contrib-knx-ultimate/htmlUtils.js"></script>
2
+
3
+ <script type="text/javascript">
4
+ RED.nodes.registerType('matter-config', {
5
+ category: 'config',
6
+ defaults: {
7
+ name: { value: "Matter Controller" },
8
+ fabricLabel: { value: "KNX-Ultimate" }
9
+ },
10
+ oneditprepare: function () {
11
+ // Go to the help panel
12
+ try {
13
+ RED.sidebar.show("help");
14
+ } catch (error) { }
15
+
16
+ var node = this;
17
+ var $pairButton = $("#matterPairButton");
18
+ var $pairingCode = $("#matter-pairing-code");
19
+ var $devicesBody = $("#matter-devices-body");
20
+ var $refreshDevices = $("#matterRefreshDevices");
21
+ var $pairSpinner = $("#matterPairSpinner");
22
+
23
+ function renderDevices(devices) {
24
+ $devicesBody.empty();
25
+ if (!Array.isArray(devices) || devices.length === 0) {
26
+ $devicesBody.append('<tr><td colspan="5" style="text-align:center; color:#999;">' + RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.no_devices") + '</td></tr>');
27
+ return;
28
+ }
29
+ devices.forEach(function (dev) {
30
+ var stateColor = dev.connectionState === 'connected' ? '#1b7d33' : '#c00';
31
+ var $tr = $('<tr></tr>');
32
+ $tr.append($('<td></td>').text(dev.name || ''));
33
+ $tr.append($('<td></td>').text((dev.vendorName || '') + (dev.productName ? ' ' + dev.productName : '')));
34
+ $tr.append($('<td style="font-family:monospace;"></td>').text(dev.nodeId));
35
+ $tr.append($('<td></td>').append($('<span></span>').css('color', stateColor).text(dev.connectionState)));
36
+ var $unpair = $('<button type="button" class="red-ui-button" style="color:#c00; border-color:#c00;"><i class="fa fa-trash"></i></button>');
37
+ $unpair.on('click', function () {
38
+ var confirmMsg = RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.unpair_confirm") + ' "' + (dev.name || dev.nodeId) + '"?';
39
+ if (!window.confirm(confirmMsg)) return;
40
+ $unpair.prop('disabled', true);
41
+ $.getJSON('KNXUltimateMatterUnpair?serverId=' + node.id + '&nodeId=' + encodeURIComponent(dev.nodeId), function (data) {
42
+ if (data && data.error) {
43
+ RED.notify(data.error, { type: 'error' });
44
+ } else {
45
+ RED.notify(RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.unpair_ok"), { type: 'success' });
46
+ }
47
+ loadDevices();
48
+ }).fail(function (jqXHR, textStatus, errorThrown) {
49
+ RED.notify('Unpair failed: ' + (errorThrown || textStatus), { type: 'error' });
50
+ $unpair.prop('disabled', false);
51
+ });
52
+ });
53
+ $tr.append($('<td style="text-align:right;"></td>').append($unpair));
54
+ $devicesBody.append($tr);
55
+ });
56
+ }
57
+
58
+ function loadDevices() {
59
+ $devicesBody.empty();
60
+ $devicesBody.append('<tr><td colspan="5" style="text-align:center;"><i class="fa fa-circle-notch fa-spin"></i></td></tr>');
61
+ $.getJSON('KNXUltimateMatterGetNodes?serverId=' + node.id + '&_=' + Date.now(), function (data) {
62
+ if (data && data.error) {
63
+ $devicesBody.empty();
64
+ $devicesBody.append('<tr><td colspan="5" style="text-align:center; color:#c00;">' + data.error + '</td></tr>');
65
+ return;
66
+ }
67
+ renderDevices(data && data.devices ? data.devices : []);
68
+ }).fail(function () {
69
+ $devicesBody.empty();
70
+ $devicesBody.append('<tr><td colspan="5" style="text-align:center; color:#999;">' + RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.deploy_first") + '</td></tr>');
71
+ });
72
+ }
73
+
74
+ $refreshDevices.on('click', function () {
75
+ loadDevices();
76
+ });
77
+
78
+ $pairButton.on('click', function () {
79
+ var code = ($pairingCode.val() || '').trim();
80
+ if (code === '') {
81
+ RED.notify(RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.pairing_code_missing"), { type: 'warning' });
82
+ return;
83
+ }
84
+ $pairButton.prop('disabled', true);
85
+ $pairSpinner.show();
86
+ var myNotification = RED.notify(RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.pairing_wait"), {
87
+ modal: false,
88
+ fixed: true,
89
+ type: 'info'
90
+ });
91
+ $.ajax({
92
+ url: 'KNXUltimateMatterPair?serverId=' + node.id + '&code=' + encodeURIComponent(code),
93
+ dataType: 'json',
94
+ timeout: 180000
95
+ }).done(function (data) {
96
+ if (data && data.error) {
97
+ RED.notify(RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.pairing_failed") + ': ' + data.error, { type: 'error', timeout: 8000 });
98
+ } else {
99
+ RED.notify(RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.pairing_ok") + ' (Node ID ' + data.nodeId + ')', { type: 'success' });
100
+ $pairingCode.val('');
101
+ loadDevices();
102
+ }
103
+ }).fail(function (jqXHR, textStatus, errorThrown) {
104
+ var msg = '';
105
+ try { msg = jqXHR.responseJSON.error; } catch (e) { msg = errorThrown || textStatus; }
106
+ RED.notify(RED._("node-red-contrib-knx-ultimate/matter-config:matter-config.properties.pairing_failed") + ': ' + msg, { type: 'error', timeout: 8000 });
107
+ }).always(function () {
108
+ if (myNotification) myNotification.close();
109
+ $pairButton.prop('disabled', false);
110
+ $pairSpinner.hide();
111
+ });
112
+ });
113
+
114
+ loadDevices();
115
+ },
116
+ oneditsave: function () {
117
+ // Return to the info tab
118
+ try {
119
+ RED.sidebar.show("info");
120
+ } catch (error) { }
121
+ },
122
+ label: function () {
123
+ return this.name || "Matter Controller";
124
+ }
125
+ });
126
+ </script>
127
+
128
+ <script type="text/html" data-template-name="matter-config">
129
+ <div class="form-row" style="margin-bottom:10px;">
130
+ <span style="color:#ff0000"><i class="fa fa-youtube"></i></span>&nbsp;<a target="_blank" href="https://www.youtube.com/playlist?list=PL9Yh1bjbLAYrU8PsVhW4xzEug2WtVFv3E"><b>KNX-Ultimate video tutorials (YouTube playlist)</b></a>
131
+ </div>
132
+
133
+ <div class="form-row">
134
+ <b><span data-i18n="matter-config.properties.title"></span></b>
135
+ <p align="center" style="margin:8px 0;">
136
+ <svg width="70" height="77" viewBox="0 8 40 44" xmlns="http://www.w3.org/2000/svg" aria-label="Matter" style="vertical-align:middle;">
137
+ <g fill="currentColor" stroke="none">
138
+ <circle cx="20" cy="30" r="4.5"/>
139
+ <g>
140
+ <rect x="18.4" y="9" width="3.2" height="11"/>
141
+ <path d="M 14.5 18.5 L 25.5 18.5 L 20 25.5 Z"/>
142
+ </g>
143
+ <g transform="rotate(120 20 30)">
144
+ <rect x="18.4" y="9" width="3.2" height="11"/>
145
+ <path d="M 14.5 18.5 L 25.5 18.5 L 20 25.5 Z"/>
146
+ </g>
147
+ <g transform="rotate(240 20 30)">
148
+ <rect x="18.4" y="9" width="3.2" height="11"/>
149
+ <path d="M 14.5 18.5 L 25.5 18.5 L 20 25.5 Z"/>
150
+ </g>
151
+ </g>
152
+ </svg>
153
+ <br/>
154
+ <span style="font-size:18px; font-weight:600; letter-spacing:1px;">matter</span>
155
+ </p>
156
+ <p><span data-i18n="matter-config.properties.intro"></span></p>
157
+ </div>
158
+
159
+ <div class="form-row">
160
+ <label for="node-config-input-name">
161
+ <i class="fa fa-tag"></i>
162
+ <span data-i18n="matter-config.properties.node-config-input-name"></span>
163
+ </label>
164
+ <input type="text" id="node-config-input-name">
165
+ </div>
166
+
167
+ <div class="form-row">
168
+ <label for="node-config-input-fabricLabel">
169
+ <i class="fa fa-id-card"></i>
170
+ <span data-i18n="matter-config.properties.fabric_label"></span>
171
+ </label>
172
+ <input type="text" id="node-config-input-fabricLabel" placeholder="KNX-Ultimate">
173
+ </div>
174
+
175
+ <hr/>
176
+
177
+ <div class="form-row">
178
+ <b><i class="fa fa-qrcode"></i>&nbsp;<span data-i18n="matter-config.properties.pairing_title"></span></b>
179
+ <p><span data-i18n="matter-config.properties.pairing_help"></span></p>
180
+ </div>
181
+
182
+ <div class="form-row">
183
+ <label for="matter-pairing-code">
184
+ <i class="fa fa-key"></i>
185
+ <span data-i18n="matter-config.properties.pairing_code"></span>
186
+ </label>
187
+ <input type="text" id="matter-pairing-code" placeholder="3497-011-2332 or MT:Y.K90...." style="width:250px;">
188
+ <button type="button" id="matterPairButton" class="red-ui-button" style="margin-left:6px; color:#1b7d33; border-color:#1b7d33;">
189
+ <i class="fa fa-link"></i>&nbsp;<span data-i18n="matter-config.properties.pair_button"></span>
190
+ </button>
191
+ <span id="matterPairSpinner" style="margin-left:6px; display:none; color:#1b7d33;">
192
+ <i class="fa fa-circle-notch fa-spin"></i>
193
+ </span>
194
+ </div>
195
+
196
+ <hr/>
197
+
198
+ <div class="form-row">
199
+ <b><i class="fa fa-microchip"></i>&nbsp;<span data-i18n="matter-config.properties.devices_title"></span></b>
200
+ <button type="button" id="matterRefreshDevices" class="red-ui-button" style="margin-left:6px; color:#1b7d33; border-color:#1b7d33;">
201
+ <i class="fa fa-refresh"></i>
202
+ </button>
203
+ </div>
204
+
205
+ <div class="form-row">
206
+ <table style="width:100%; border-collapse:collapse;" class="red-ui-editableList">
207
+ <thead>
208
+ <tr style="border-bottom:1px solid #ccc; text-align:left;">
209
+ <th data-i18n="matter-config.properties.col_name"></th>
210
+ <th data-i18n="matter-config.properties.col_product"></th>
211
+ <th>Node ID</th>
212
+ <th data-i18n="matter-config.properties.col_state"></th>
213
+ <th></th>
214
+ </tr>
215
+ </thead>
216
+ <tbody id="matter-devices-body"></tbody>
217
+ </table>
218
+ </div>
219
+
220
+ <br/>
221
+ <br/>
222
+ <br/>
223
+ <br/>
224
+ <br/>
225
+ <br/>
226
+ </script>