node-red-contrib-knx-ultimate 6.3.13 → 6.3.14

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 CHANGED
@@ -6,6 +6,10 @@
6
6
 
7
7
  # CHANGELOG
8
8
 
9
+ **Version 6.3.14** - August 2026<br/>
10
+
11
+ - **Expose KNX to Matter — Room Air Conditioner and Door Lock**: added native Matter **Room Air Conditioner** (`0x0072`) and **Door Lock** (`0x000A`) profiles. A room AC combines On/Off, current temperature, heating/cooling setpoints and fan speed in one endpoint; a door lock maps separate Lock/Unlock command and Locked/Unlocked status GAs. Both profiles support KNX or flow-backed state updates without feedback loops, with editor help and documentation in EN/IT/DE/FR/ES/zh-CN.<br/>
12
+
9
13
  **Version 6.3.13** - August 2026<br/>
10
14
 
11
15
  - **Matter**: promoted Matter Controller, Matter Bridge and Expose KNX to Matter from BETA to production-ready status; removed BETA labels and experimental warnings from the palette, editor help, examples and multilingual documentation while retaining pairing, backup and endpoint-structure safety guidance.<br/>
@@ -54,6 +54,21 @@
54
54
  { key: 'CoolingSetpoint', labelKey: 'fn_coolingsetpoint_cmd', dptPrefix: '9.', fallbackDpt: '9.001' },
55
55
  { key: 'CoolingSetpointStatus', labelKey: 'fn_coolingsetpoint_status', dptPrefix: '9.', fallbackDpt: '9.001' }
56
56
  ],
57
+ roomairconditioner: [
58
+ { key: 'OnOff', labelKey: 'fn_onoff_cmd', dptPrefix: '1.', fallbackDpt: '1.001' },
59
+ { key: 'OnOffStatus', labelKey: 'fn_onoff_status', dptPrefix: '1.', fallbackDpt: '1.001' },
60
+ { key: 'CurrentTemp', labelKey: 'fn_currenttemp', dptPrefix: '9.', fallbackDpt: '9.001' },
61
+ { key: 'Setpoint', labelKey: 'fn_setpoint_cmd', dptPrefix: '9.', fallbackDpt: '9.001' },
62
+ { key: 'SetpointStatus', labelKey: 'fn_setpoint_status', dptPrefix: '9.', fallbackDpt: '9.001' },
63
+ { key: 'CoolingSetpoint', labelKey: 'fn_coolingsetpoint_cmd', dptPrefix: '9.', fallbackDpt: '9.001' },
64
+ { key: 'CoolingSetpointStatus', labelKey: 'fn_coolingsetpoint_status', dptPrefix: '9.', fallbackDpt: '9.001' },
65
+ { key: 'FanSpeed', labelKey: 'fn_fanspeed_cmd', dptPrefix: '5.', fallbackDpt: '5.001' },
66
+ { key: 'FanSpeedStatus', labelKey: 'fn_fanspeed_status', dptPrefix: '5.', fallbackDpt: '5.001' }
67
+ ],
68
+ doorlock: [
69
+ { key: 'Lock', labelKey: 'fn_lock_cmd', dptPrefix: '1.', fallbackDpt: '1.001' },
70
+ { key: 'LockStatus', labelKey: 'fn_lock_status', dptPrefix: '1.', fallbackDpt: '1.001' }
71
+ ],
57
72
  smokecoalarm: [
58
73
  { key: 'Smoke', labelKey: 'fn_smoke_ga', dptPrefix: '1.', fallbackDpt: '1.005' },
59
74
  { key: 'CO', labelKey: 'fn_co_ga', dptPrefix: '1.', fallbackDpt: '1.005' }
@@ -66,7 +81,7 @@
66
81
  ],
67
82
  robotvacuum: [] // Flow-only: no GA fields, controlled via the node PINs
68
83
  };
69
- const TYPE_ORDER = ['onofflight', 'dimmablelight', 'rgblight', 'colortemperaturelight', 'onoffplug', 'windowcovering', 'thermostat', 'fan', 'temperaturesensor', 'humiditysensor', 'lightsensor', 'occupancysensor', 'contactsensor', 'smokecoalarm', 'waterleakdetector', 'airqualitysensor', 'robotvacuum'];
84
+ const TYPE_ORDER = ['onofflight', 'dimmablelight', 'rgblight', 'colortemperaturelight', 'onoffplug', 'windowcovering', 'thermostat', 'roomairconditioner', 'fan', 'doorlock', 'temperaturesensor', 'humiditysensor', 'lightsensor', 'occupancysensor', 'contactsensor', 'smokecoalarm', 'waterleakdetector', 'airqualitysensor', 'robotvacuum'];
70
85
  const FLOW_HELP = {
71
86
  onofflight: { input: [['onoff', true]], output: [['onoff', true]] },
72
87
  onoffplug: { input: [['onoff', true]], output: [['onoff', true]] },
@@ -80,6 +95,8 @@
80
95
  contactsensor: { input: [['contact', true]], output: [] },
81
96
  windowcovering: { input: [['position', 35]], output: [['updown', false], ['position', 35], ['stop', true]] },
82
97
  thermostat: { input: [['currenttemp', 21.5], ['setpoint', 21], ['coolingsetpoint', 24]], output: [['setpoint', 21], ['coolingsetpoint', 24]] },
98
+ roomairconditioner: { input: [['onoff', true], ['currenttemp', 25.5], ['setpoint', 21], ['coolingsetpoint', 24], ['fanspeed', 60]], output: [['onoff', true], ['setpoint', 21], ['coolingsetpoint', 24], ['fanspeed', 60]] },
99
+ doorlock: { input: [['lock', true]], output: [['lock', true]] },
83
100
  smokecoalarm: { input: [['smoke', true], ['co', true]], output: [] },
84
101
  waterleakdetector: { input: [['leak', true]], output: [] },
85
102
  airqualitysensor: { input: [['co2', 850]], output: [] },
@@ -379,7 +396,7 @@
379
396
  const isCover = selected === 'windowcovering';
380
397
  // The Alexa duplicate-command dedup applies to any device that emits On/Off or
381
398
  // level commands (plain on/off lights and plugs included), not just dimmers.
382
- const hasCommand = selected === 'onofflight' || selected === 'onoffplug' || hasLevel;
399
+ const hasCommand = selected === 'onofflight' || selected === 'onoffplug' || selected === 'roomairconditioner' || hasLevel;
383
400
  $advancedRows.toggle(hasCommand || isCover);
384
401
  $commandAdvancedRows.toggle(hasCommand);
385
402
  $dimmerAdvancedRows.toggle(hasLevel);
@@ -55,6 +55,21 @@ module.exports = function (RED) {
55
55
  { fn: 'coolingsetpoint', direction: 'command', ga: 'gaCoolingSetpoint', dpt: 'dptCoolingSetpoint', fallbackDpt: '9.001' },
56
56
  { fn: 'coolingsetpoint', direction: 'status', ga: 'gaCoolingSetpointStatus', dpt: 'dptCoolingSetpointStatus', fallbackDpt: '9.001' }
57
57
  ],
58
+ roomairconditioner: [
59
+ { fn: 'onoff', direction: 'command', ga: 'gaOnOff', dpt: 'dptOnOff', fallbackDpt: '1.001' },
60
+ { fn: 'onoff', direction: 'status', ga: 'gaOnOffStatus', dpt: 'dptOnOffStatus', fallbackDpt: '1.001' },
61
+ { fn: 'currenttemp', direction: 'status', ga: 'gaCurrentTemp', dpt: 'dptCurrentTemp', fallbackDpt: '9.001' },
62
+ { fn: 'setpoint', direction: 'command', ga: 'gaSetpoint', dpt: 'dptSetpoint', fallbackDpt: '9.001' },
63
+ { fn: 'setpoint', direction: 'status', ga: 'gaSetpointStatus', dpt: 'dptSetpointStatus', fallbackDpt: '9.001' },
64
+ { fn: 'coolingsetpoint', direction: 'command', ga: 'gaCoolingSetpoint', dpt: 'dptCoolingSetpoint', fallbackDpt: '9.001' },
65
+ { fn: 'coolingsetpoint', direction: 'status', ga: 'gaCoolingSetpointStatus', dpt: 'dptCoolingSetpointStatus', fallbackDpt: '9.001' },
66
+ { fn: 'fanspeed', direction: 'command', ga: 'gaFanSpeed', dpt: 'dptFanSpeed', fallbackDpt: '5.001' },
67
+ { fn: 'fanspeed', direction: 'status', ga: 'gaFanSpeedStatus', dpt: 'dptFanSpeedStatus', fallbackDpt: '5.001' }
68
+ ],
69
+ doorlock: [
70
+ { fn: 'lock', direction: 'command', ga: 'gaLock', dpt: 'dptLock', fallbackDpt: '1.001' },
71
+ { fn: 'lock', direction: 'status', ga: 'gaLockStatus', dpt: 'dptLockStatus', fallbackDpt: '1.001' }
72
+ ],
58
73
  smokecoalarm: [
59
74
  { fn: 'smoke', direction: 'status', ga: 'gaSmoke', dpt: 'dptSmoke', fallbackDpt: '1.005' },
60
75
  { fn: 'co', direction: 'status', ga: 'gaCO', dpt: 'dptCO', fallbackDpt: '1.005' }
@@ -275,9 +290,9 @@ module.exports = function (RED) {
275
290
  coverSliderDebounceMs: node.coverSliderDebounceMs,
276
291
  commandDedupMs: node.commandDedupMs,
277
292
  coverUpdateMode: node.coverUpdateMode,
278
- // Thermostat only: whether a heating/cooling setpoint GA (command or status) was
279
- // configured, so the bridge engine can build a Heating-only, Cooling-only or dual
280
- // ThermostatServer without a dedicated "mode" dropdown in the editor.
293
+ // Thermostat/Room Air Conditioner: whether a heating/cooling setpoint GA (command
294
+ // or status) was configured, so the bridge engine can build a Heating-only,
295
+ // Cooling-only or dual ThermostatServer without a dedicated mode dropdown.
281
296
  hasHeatingSetpoint: devConfigValue('gaSetpoint') !== '' || devConfigValue('gaSetpointStatus') !== '',
282
297
  hasCoolingSetpoint: devConfigValue('gaCoolingSetpoint') !== '' || devConfigValue('gaCoolingSetpointStatus') !== ''
283
298
  })
@@ -27,7 +27,9 @@ Das Ändern des Gerätetyps nach der Kopplung der Bridge verändert die Matter-E
27
27
  | Tunable-White-Licht | + Farbtemperatur Befehls-/Status-GA in Kelvin (DPT 7.600) |
28
28
  | Rollladen / Jalousie | Auf/Ab (DPT 1.008), Stopp (DPT 1.017), Position % Befehl/Status (DPT 5.001), optionale Positionsumkehr |
29
29
  | Thermostat (Heizung und/oder Kühlung) | GA aktuelle Temperatur, Sollwert Befehls-/Status-GA (DPT 9.001). Mit zusätzlichem Kühl-Sollwert Befehls-/Status-GA wird auch der Kühlmodus (Doppel-Sollwert) bereitgestellt |
30
+ | Raumklimagerät | Ein Matter-Gerät kombiniert Ein/Aus (DPT 1.001), aktuelle Temperatur und Heiz-/Kühl-Sollwerte (DPT 9.001) sowie Lüftergeschwindigkeit % (DPT 5.001), jeweils als Befehls-/Status-GA |
30
31
  | Ventilator / Lüftung | Geschwindigkeit % Befehls-/Status-GA (DPT 5.001) |
32
+ | Türschloss | Verriegeln/Entriegeln Befehls-GA und Verriegelt/Entriegelt Status-GA (DPT 1.001; `true` = verriegelt) |
31
33
  | Sensoren (Temperatur, Feuchte, Licht, Präsenz, Kontakt) | Je eine Status-GA |
32
34
  | Rauch-/CO-Melder | Rauchalarm Status-GA + optionale CO-Alarm Status-GA (DPT 1.005): kritische Benachrichtigungen auf dem Telefon |
33
35
  | Wasserleckmelder | Leckage Status-GA (DPT 1.005) |
@@ -45,7 +47,7 @@ Diese Optionen werden nur angezeigt, wenn sie zum gewählten Typ passen. Dimmbar
45
47
 
46
48
  Der PIN-Selektor befindet sich außerhalb der Editor-Tabs. Nach dem Aktivieren erscheint direkt darunter ein kontextbezogener Bereich **Flow-Eingang/-Ausgang** mit kopierbaren Beispielen für den ausgewählten Gerätetyp:
47
49
 
48
- - **Input**: aktualisiere den Matter-Zustand aus dem Flow, ohne den KNX-Bus: `msg.payload = { function: "onoff", value: true }` (`function` ist eine von `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `fanspeed`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Nützlich, um im Flow berechnete Werte (z.B. einen virtuellen Sensor) für Alexa & Co. bereitzustellen.
50
+ - **Input**: aktualisiere den Matter-Zustand aus dem Flow, ohne den KNX-Bus: `msg.payload = { function: "onoff", value: true }` (`function` ist eine von `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `coolingsetpoint`, `fanspeed`, `lock`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Nützlich, um im Flow berechnete Werte (z.B. einen virtuellen Sensor) für Alexa & Co. bereitzustellen.
49
51
  - **Output**: Jeder von einem Matter-Controller empfangene Befehl wird an den Flow weitergeleitet: `msg.topic` = Gerätename, `msg.payload` = Wert, `msg.matter` = der rohe Befehl. Ein Gerät ohne Befehls-GAs wird zum **Nur-Flow-Gerät**.
50
52
 
51
53
  ## Hinweise
@@ -74,7 +74,9 @@
74
74
  "fn_leak_ga": "Leckage Status-GA",
75
75
  "fn_co2_ga": "CO2 Status-GA (ppm, DPT 9.008)",
76
76
  "fn_fanspeed_cmd": "Lüftergeschwindigkeit % Befehls-GA",
77
- "fn_fanspeed_status": "Lüftergeschwindigkeit % Status-GA"
77
+ "fn_fanspeed_status": "Lüftergeschwindigkeit % Status-GA",
78
+ "fn_lock_cmd": "Verriegeln/Entriegeln Befehls-GA",
79
+ "fn_lock_status": "Verriegelt/Entriegelt Status-GA"
78
80
  },
79
81
  "types": {
80
82
  "onofflight": "Ein/Aus-Licht",
@@ -89,7 +91,9 @@
89
91
  "contactsensor": "Kontaktsensor",
90
92
  "windowcovering": "Rollladen / Jalousie",
91
93
  "thermostat": "Thermostat (Heizung)",
94
+ "roomairconditioner": "Raumklimagerät",
92
95
  "fan": "Ventilator / Lüftung",
96
+ "doorlock": "Türschloss",
93
97
  "smokecoalarm": "Rauch-/CO-Melder",
94
98
  "waterleakdetector": "Wasserleckmelder",
95
99
  "airqualitysensor": "Luftqualitätssensor (CO2)",
@@ -27,7 +27,9 @@ Changing the device type after the bridge has been paired changes the Matter end
27
27
  | Tunable white light | + Color temperature command/status GA in Kelvin (DPT 7.600) |
28
28
  | Cover / Shutter | Up/Down (DPT 1.008), Stop (DPT 1.017), Position % command/status (DPT 5.001), optional position inversion |
29
29
  | Thermostat (heating and/or cooling) | Current temperature GA, Setpoint command/status GA (DPT 9.001). Optionally add a Cooling setpoint command/status GA to also expose a Cool mode (dual-setpoint) |
30
+ | Room air conditioner | One Matter device combining On/Off (DPT 1.001), current temperature and heating/cooling setpoints (DPT 9.001), and fan speed % (DPT 5.001) command/status GAs |
30
31
  | Fan / Ventilation | Fan speed % command/status GA (DPT 5.001) |
32
+ | Door lock | Lock/Unlock command GA and Locked/Unlocked status GA (DPT 1.001; `true` = locked) |
31
33
  | Sensors (temperature, humidity, light, occupancy, contact) | One status GA each |
32
34
  | Smoke/CO alarm | Smoke alarm status GA + optional CO alarm status GA (DPT 1.005): critical notifications on the phone |
33
35
  | Water leak detector | Leak status GA (DPT 1.005) |
@@ -45,7 +47,7 @@ These options are hidden unless they apply to the selected type. Dimmable device
45
47
 
46
48
  The PIN selector is outside the editor tabs. Enabling it reveals a contextual **Flow input/output** section directly below, with copyable examples filtered to the selected device type:
47
49
 
48
- - **Input**: update the Matter state from the flow, without the KNX bus: `msg.payload = { function: "onoff", value: true }` (`function` is one of `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `fanspeed`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Useful to expose flow-computed values (e.g. a virtual sensor) to Alexa & Co.
50
+ - **Input**: update the Matter state from the flow, without the KNX bus: `msg.payload = { function: "onoff", value: true }` (`function` is one of `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `coolingsetpoint`, `fanspeed`, `lock`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Useful to expose flow-computed values (e.g. a virtual sensor) to Alexa & Co.
49
51
  - **Output**: every command received from a Matter controller is forwarded to the flow: `msg.topic` = device name, `msg.payload` = value, `msg.matter` = the raw command. A device without command GAs becomes a **flow-only device**.
50
52
 
51
53
  ## Notes
@@ -74,7 +74,9 @@
74
74
  "fn_leak_ga": "Leak status GA",
75
75
  "fn_co2_ga": "CO2 status GA (ppm, DPT 9.008)",
76
76
  "fn_fanspeed_cmd": "Fan speed % command GA",
77
- "fn_fanspeed_status": "Fan speed % status GA"
77
+ "fn_fanspeed_status": "Fan speed % status GA",
78
+ "fn_lock_cmd": "Lock/Unlock command GA",
79
+ "fn_lock_status": "Locked/Unlocked status GA"
78
80
  },
79
81
  "types": {
80
82
  "onofflight": "On/Off light",
@@ -89,7 +91,9 @@
89
91
  "contactsensor": "Contact sensor",
90
92
  "windowcovering": "Cover / Shutter",
91
93
  "thermostat": "Thermostat (heating)",
94
+ "roomairconditioner": "Room air conditioner",
92
95
  "fan": "Fan / Ventilation",
96
+ "doorlock": "Door lock",
93
97
  "smokecoalarm": "Smoke/CO alarm",
94
98
  "waterleakdetector": "Water leak detector",
95
99
  "airqualitysensor": "Air quality sensor (CO2)",
@@ -27,7 +27,9 @@ Cambiar el tipo de dispositivo después de emparejar el bridge modifica la estru
27
27
  | Luz blanco dinámico | + GA comando/estado temperatura de color en Kelvin (DPT 7.600) |
28
28
  | Persiana / Cortina | Subir/Bajar (DPT 1.008), Stop (DPT 1.017), posición % comando/estado (DPT 5.001), inversión de posición opcional |
29
29
  | Termostato (calefacción y/o refrigeración) | GA temperatura actual, GA comando/estado consigna (DPT 9.001). Añadiendo también la GA comando/estado consigna de refrigeración se expone también el modo Cool (doble consigna) |
30
+ | Aire acondicionado de habitación | Un dispositivo Matter combina GA comando/estado On/Off (DPT 1.001), temperatura actual y consignas de calefacción/refrigeración (DPT 9.001), y velocidad del ventilador % (DPT 5.001) |
30
31
  | Ventilador / VMC | GA comando/estado velocidad % (DPT 5.001) |
32
+ | Cerradura de puerta | GA comando bloqueo/desbloqueo y GA estado bloqueado/desbloqueado (DPT 1.001; `true` = bloqueada) |
31
33
  | Sensores (temperatura, humedad, luz, presencia, contacto) | Una GA de estado cada uno |
32
34
  | Alarma humo/CO | GA estado alarma de humo + GA estado alarma CO opcional (DPT 1.005): notificaciones críticas en el teléfono |
33
35
  | Detector de fugas de agua | GA estado fuga (DPT 1.005) |
@@ -45,7 +47,7 @@ Estas opciones solo aparecen cuando tienen sentido para el tipo seleccionado. Lo
45
47
 
46
48
  El selector de PINes está fuera de las pestañas del editor. Al activarlo aparece justo debajo una sección contextual **Entrada/salida del flow**, con ejemplos copiables filtrados según el tipo de dispositivo:
47
49
 
48
- - **Entrada**: actualiza el estado Matter desde el flow, sin pasar por el bus KNX: `msg.payload = { function: "onoff", value: true }` (`function` es una de `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `fanspeed`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Útil para exponer a Alexa y cía. valores calculados en el flow (p.ej. un sensor virtual).
50
+ - **Entrada**: actualiza el estado Matter desde el flow, sin pasar por el bus KNX: `msg.payload = { function: "onoff", value: true }` (`function` es una de `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `coolingsetpoint`, `fanspeed`, `lock`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Útil para exponer a Alexa y cía. valores calculados en el flow (p.ej. un sensor virtual).
49
51
  - **Salida**: cada comando recibido de un controlador Matter se reenvía al flow: `msg.topic` = nombre del dispositivo, `msg.payload` = valor, `msg.matter` = el comando en bruto. Un dispositivo sin GA de comando se convierte en un **dispositivo solo-flow**.
50
52
 
51
53
  ## Notas
@@ -74,7 +74,9 @@
74
74
  "fn_leak_ga": "GA estado fuga de agua",
75
75
  "fn_co2_ga": "GA estado CO2 (ppm, DPT 9.008)",
76
76
  "fn_fanspeed_cmd": "GA comando velocidad ventilador %",
77
- "fn_fanspeed_status": "GA estado velocidad ventilador %"
77
+ "fn_fanspeed_status": "GA estado velocidad ventilador %",
78
+ "fn_lock_cmd": "GA comando bloqueo/desbloqueo",
79
+ "fn_lock_status": "GA estado bloqueado/desbloqueado"
78
80
  },
79
81
  "types": {
80
82
  "onofflight": "Luz On/Off",
@@ -89,7 +91,9 @@
89
91
  "contactsensor": "Sensor de contacto",
90
92
  "windowcovering": "Persiana / Cortina",
91
93
  "thermostat": "Termostato (calefacción)",
94
+ "roomairconditioner": "Aire acondicionado de habitación",
92
95
  "fan": "Ventilador / VMC",
96
+ "doorlock": "Cerradura de puerta",
93
97
  "smokecoalarm": "Alarma humo/CO",
94
98
  "waterleakdetector": "Detector de fugas de agua",
95
99
  "airqualitysensor": "Sensor de calidad del aire (CO2)",
@@ -27,7 +27,9 @@ Changer le type d'appareil après l'appairage du bridge modifie la structure de
27
27
  | Lumière blanc dynamique | + GA commande/état température de couleur en Kelvin (DPT 7.600) |
28
28
  | Volet / Store | Monter/Descendre (DPT 1.008), Stop (DPT 1.017), position % commande/état (DPT 5.001), inversion de position optionnelle |
29
29
  | Thermostat (chauffage et/ou rafraîchissement) | GA température actuelle, GA commande/état consigne (DPT 9.001). En ajoutant aussi la GA commande/état consigne rafraîchissement, le mode Cool (double consigne) est également exposé |
30
+ | Climatiseur individuel | Un appareil Matter combine les GA commande/état On/Off (DPT 1.001), température actuelle et consignes chauffage/rafraîchissement (DPT 9.001), et vitesse ventilateur % (DPT 5.001) |
30
31
  | Ventilateur / VMC | GA commande/état vitesse % (DPT 5.001) |
32
+ | Serrure de porte | GA commande verrouillage/déverrouillage et GA état verrouillé/déverrouillé (DPT 1.001 ; `true` = verrouillée) |
31
33
  | Capteurs (température, humidité, lumière, présence, contact) | Une GA d'état chacun |
32
34
  | Détecteur fumée/CO | GA état alarme fumée + GA état alarme CO optionnelle (DPT 1.005) : notifications critiques sur le téléphone |
33
35
  | Détecteur de fuite d'eau | GA état fuite (DPT 1.005) |
@@ -45,7 +47,7 @@ Ces options ne s'affichent que lorsqu'elles s'appliquent au type sélectionné.
45
47
 
46
48
  Le sélecteur de PIN se trouve hors des onglets de l'éditeur. Son activation affiche juste dessous une section contextuelle **Entrée/sortie du flow**, avec des exemples copiables filtrés selon le type d'appareil :
47
49
 
48
- - **Entrée** : mettez à jour l'état Matter depuis le flux, sans passer par le bus KNX : `msg.payload = { function: "onoff", value: true }` (`function` est l'une de `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `fanspeed`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Utile pour exposer à Alexa & Co. des valeurs calculées dans le flux (ex. un capteur virtuel).
50
+ - **Entrée** : mettez à jour l'état Matter depuis le flux, sans passer par le bus KNX : `msg.payload = { function: "onoff", value: true }` (`function` est l'une de `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `coolingsetpoint`, `fanspeed`, `lock`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Utile pour exposer à Alexa & Co. des valeurs calculées dans le flux (ex. un capteur virtuel).
49
51
  - **Sortie** : chaque commande reçue d'un contrôleur Matter est transmise au flux : `msg.topic` = nom de l'appareil, `msg.payload` = valeur, `msg.matter` = la commande brute. Un appareil sans GA de commande devient un **appareil flow uniquement**.
50
52
 
51
53
  ## Notes
@@ -74,7 +74,9 @@
74
74
  "fn_leak_ga": "GA état fuite d'eau",
75
75
  "fn_co2_ga": "GA état CO2 (ppm, DPT 9.008)",
76
76
  "fn_fanspeed_cmd": "GA commande vitesse ventilateur %",
77
- "fn_fanspeed_status": "GA état vitesse ventilateur %"
77
+ "fn_fanspeed_status": "GA état vitesse ventilateur %",
78
+ "fn_lock_cmd": "GA commande verrouillage/déverrouillage",
79
+ "fn_lock_status": "GA état verrouillé/déverrouillé"
78
80
  },
79
81
  "types": {
80
82
  "onofflight": "Lumière On/Off",
@@ -89,7 +91,9 @@
89
91
  "contactsensor": "Capteur de contact",
90
92
  "windowcovering": "Volet / Store",
91
93
  "thermostat": "Thermostat (chauffage)",
94
+ "roomairconditioner": "Climatiseur individuel",
92
95
  "fan": "Ventilateur / VMC",
96
+ "doorlock": "Serrure de porte",
93
97
  "smokecoalarm": "Détecteur fumée/CO",
94
98
  "waterleakdetector": "Détecteur de fuite d'eau",
95
99
  "airqualitysensor": "Capteur de qualité d'air (CO2)",
@@ -27,7 +27,9 @@ Cambiare il tipo dispositivo dopo l'abbinamento del bridge modifica la struttura
27
27
  | Luce bianco dinamico | + GA comando/stato temperatura colore in Kelvin (DPT 7.600) |
28
28
  | Tapparella / Tenda | Su/Giù (DPT 1.008), Stop (DPT 1.017), posizione % comando/stato (DPT 5.001), inversione posizione opzionale |
29
29
  | Termostato (riscaldamento e/o raffrescamento) | GA temperatura attuale, GA comando/stato setpoint (DPT 9.001). Aggiungendo anche il GA comando/stato setpoint raffrescamento si espone anche la modalità Cool (doppio setpoint) |
30
+ | Condizionatore ambiente | Un unico dispositivo Matter che combina GA comando/stato On/Off (DPT 1.001), temperatura attuale e setpoint caldo/freddo (DPT 9.001), e velocità ventola % (DPT 5.001) |
30
31
  | Ventola / VMC | GA comando/stato velocità % (DPT 5.001) |
32
+ | Serratura porta | GA comando blocco/sblocco e GA stato bloccato/sbloccato (DPT 1.001; `true` = bloccata) |
31
33
  | Sensori (temperatura, umidità, luminosità, presenza, contatto) | Un GA di stato ciascuno |
32
34
  | Rilevatore fumo/CO | GA stato allarme fumo + GA stato allarme CO opzionale (DPT 1.005): notifiche critiche sul telefono |
33
35
  | Rilevatore allagamento | GA stato allagamento (DPT 1.005) |
@@ -45,7 +47,7 @@ Queste opzioni sono nascoste finché non servono al tipo selezionato. I disposit
45
47
 
46
48
  Il selettore dei PIN si trova fuori dalle TAB dell'editor. Abilitandolo compare subito sotto una sezione contestuale **Input/output del flow**, con esempi copiabili filtrati per il tipo di dispositivo selezionato:
47
49
 
48
- - **Input**: aggiorna lo stato Matter dal flow, senza passare dal bus KNX: `msg.payload = { function: "onoff", value: true }` (`function` è una tra `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `fanspeed`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Utile per esporre ad Alexa & Co. valori calcolati nel flow (es. un sensore virtuale).
50
+ - **Input**: aggiorna lo stato Matter dal flow, senza passare dal bus KNX: `msg.payload = { function: "onoff", value: true }` (`function` è una tra `onoff`, `level`, `rgb`, `colortemp`, `position`, `temperature`, `humidity`, `illuminance`, `occupancy`, `contact`, `currenttemp`, `setpoint`, `coolingsetpoint`, `fanspeed`, `lock`, `smoke`, `co`, `leak`, `co2`, `rvcstate`, `rvcmode`). Utile per esporre ad Alexa & Co. valori calcolati nel flow (es. un sensore virtuale).
49
51
  - **Output**: ogni comando ricevuto da un controller Matter viene inoltrato al flow: `msg.topic` = nome del dispositivo, `msg.payload` = valore, `msg.matter` = il comando grezzo. Un dispositivo senza GA di comando diventa un **dispositivo solo-flow**.
50
52
 
51
53
  ## Note
@@ -74,7 +74,9 @@
74
74
  "fn_leak_ga": "GA stato allagamento",
75
75
  "fn_co2_ga": "GA stato CO2 (ppm, DPT 9.008)",
76
76
  "fn_fanspeed_cmd": "GA comando velocità ventola %",
77
- "fn_fanspeed_status": "GA stato velocità ventola %"
77
+ "fn_fanspeed_status": "GA stato velocità ventola %",
78
+ "fn_lock_cmd": "GA comando blocco/sblocco",
79
+ "fn_lock_status": "GA stato bloccato/sbloccato"
78
80
  },
79
81
  "types": {
80
82
  "onofflight": "Luce On/Off",
@@ -89,7 +91,9 @@
89
91
  "contactsensor": "Sensore contatto",
90
92
  "windowcovering": "Tapparella / Tenda",
91
93
  "thermostat": "Termostato (riscaldamento)",
94
+ "roomairconditioner": "Condizionatore ambiente",
92
95
  "fan": "Ventola / VMC",
96
+ "doorlock": "Serratura porta",
93
97
  "smokecoalarm": "Rilevatore fumo/CO",
94
98
  "waterleakdetector": "Rilevatore allagamento",
95
99
  "airqualitysensor": "Sensore qualità aria (CO2)",
@@ -27,7 +27,9 @@
27
27
  | 色温灯 | + 色温命令/状态 GA,开尔文 (DPT 7.600) |
28
28
  | 窗帘/卷帘 | 上/下 (DPT 1.008)、停止 (DPT 1.017)、位置 % 命令/状态 (DPT 5.001)、可选位置反转 |
29
29
  | 温控器(采暖和/或制冷) | 当前温度 GA、设定点命令/状态 GA (DPT 9.001)。如再配置制冷设定点命令/状态 GA,则同时提供 Cool 模式(双设定点) |
30
+ | 房间空调 | 一个 Matter 设备组合开/关命令/状态 GA (DPT 1.001)、当前温度与采暖/制冷设定点 (DPT 9.001),以及风扇速度 % 命令/状态 GA (DPT 5.001) |
30
31
  | 风扇/新风 | 速度 % 命令/状态 GA (DPT 5.001) |
32
+ | 门锁 | 锁定/解锁命令 GA 和锁定/解锁状态 GA (DPT 1.001;`true` = 已锁定) |
31
33
  | 传感器(温度、湿度、光照、人体、门磁) | 各一个状态 GA |
32
34
  | 烟雾/CO 报警器 | 烟雾报警状态 GA + 可选 CO 报警状态 GA (DPT 1.005):手机上的紧急通知 |
33
35
  | 漏水探测器 | 漏水状态 GA (DPT 1.005) |
@@ -45,7 +47,7 @@
45
47
 
46
48
  引脚选择器位于编辑器选项卡之外。启用后,其下方会显示与所选设备类型匹配的 **Flow 输入/输出** 区域,并提供可复制示例:
47
49
 
48
- - **输入**:从 Flow 更新 Matter 状态,无需经过 KNX 总线:`msg.payload = { function: "onoff", value: true }`(`function` 为 `onoff`、`level`、`rgb`、`colortemp`、`position`、`temperature`、`humidity`、`illuminance`、`occupancy`、`contact`、`currenttemp`、`setpoint`、`fanspeed`、`smoke`、`co`、`leak`、`co2`、`rvcstate`、`rvcmode` 之一)。适合把 Flow 中计算的值(如虚拟传感器)公开给 Alexa 等。
50
+ - **输入**:从 Flow 更新 Matter 状态,无需经过 KNX 总线:`msg.payload = { function: "onoff", value: true }`(`function` 为 `onoff`、`level`、`rgb`、`colortemp`、`position`、`temperature`、`humidity`、`illuminance`、`occupancy`、`contact`、`currenttemp`、`setpoint`、`coolingsetpoint`、`fanspeed`、`lock`、`smoke`、`co`、`leak`、`co2`、`rvcstate`、`rvcmode` 之一)。适合把 Flow 中计算的值(如虚拟传感器)公开给 Alexa 等。
49
51
  - **输出**:从 Matter 控制器收到的每个命令都转发到 Flow:`msg.topic` = 设备名称,`msg.payload` = 值,`msg.matter` = 原始命令。没有命令 GA 的设备成为**纯 Flow 设备**。
50
52
 
51
53
  ## 说明
@@ -74,7 +74,9 @@
74
74
  "fn_leak_ga": "漏水状态 GA",
75
75
  "fn_co2_ga": "CO2 状态 GA(ppm,DPT 9.008)",
76
76
  "fn_fanspeed_cmd": "风扇速度 % 命令 GA",
77
- "fn_fanspeed_status": "风扇速度 % 状态 GA"
77
+ "fn_fanspeed_status": "风扇速度 % 状态 GA",
78
+ "fn_lock_cmd": "锁定/解锁 命令 GA",
79
+ "fn_lock_status": "锁定/解锁 状态 GA"
78
80
  },
79
81
  "types": {
80
82
  "onofflight": "开关灯",
@@ -89,7 +91,9 @@
89
91
  "contactsensor": "门磁传感器",
90
92
  "windowcovering": "窗帘 / 卷帘",
91
93
  "thermostat": "温控器(采暖)",
94
+ "roomairconditioner": "房间空调",
92
95
  "fan": "风扇 / 新风",
96
+ "doorlock": "门锁",
93
97
  "smokecoalarm": "烟雾/CO 报警器",
94
98
  "waterleakdetector": "漏水探测器",
95
99
  "airqualitysensor": "空气质量传感器 (CO2)",
@@ -28,6 +28,11 @@ import { AirQualitySensorDevice } from '@matter/main/devices/air-quality-sensor'
28
28
  import { AirQualityServer } from '@matter/main/behaviors/air-quality'
29
29
  import { CarbonDioxideConcentrationMeasurementServer } from '@matter/main/behaviors/carbon-dioxide-concentration-measurement'
30
30
  import { FanDevice } from '@matter/main/devices/fan'
31
+ import { FanControlServer } from '@matter/main/behaviors/fan-control'
32
+ import { RoomAirConditionerDevice } from '@matter/main/devices/room-air-conditioner'
33
+ import { DoorLockDevice } from '@matter/main/devices/door-lock'
34
+ import { DoorLockServer } from '@matter/main/behaviors/door-lock'
35
+ import { DoorLock } from '@matter/main/clusters/door-lock'
31
36
  import { RoboticVacuumCleanerDevice } from '@matter/main/devices/robotic-vacuum-cleaner'
32
37
  import { RvcRunModeServer } from '@matter/main/behaviors/rvc-run-mode'
33
38
  import { RvcOperationalStateServer } from '@matter/main/behaviors/rvc-operational-state'
@@ -53,6 +58,8 @@ const BRIDGE_TYPES = {
53
58
  // Custom construction (dedicated server behaviors), handled in createBridgedEndpoint
54
59
  windowcovering: { device: null, functions: ['position'], commandFunctions: [] },
55
60
  thermostat: { device: null, functions: ['currenttemp', 'setpoint', 'coolingsetpoint'], commandFunctions: [] },
61
+ roomairconditioner: { device: null, functions: ['onoff', 'currenttemp', 'setpoint', 'coolingsetpoint', 'fanspeed'], commandFunctions: [] },
62
+ doorlock: { device: null, functions: ['lock'], commandFunctions: [] },
56
63
  rgblight: { device: null, functions: ['onoff', 'level', 'rgb'], commandFunctions: ['onoff', 'level'] },
57
64
  colortemperaturelight: { device: null, functions: ['onoff', 'level', 'colortemp'], commandFunctions: ['onoff', 'level'] },
58
65
  smokecoalarm: { device: null, functions: ['smoke', 'co'], commandFunctions: [] },
@@ -124,6 +131,8 @@ function knxValueToMatterPatch (def, fn, value) {
124
131
  }
125
132
  case 'onoff':
126
133
  return { onOff: { onOff: truthy(value) } }
134
+ case 'lock':
135
+ return { doorLock: { lockState: truthy(value) ? DoorLock.LockState.Locked : DoorLock.LockState.Unlocked } }
127
136
  case 'level': {
128
137
  const percent = Number(value)
129
138
  if (Number.isNaN(percent)) return undefined
@@ -281,6 +290,76 @@ function createRawColorControlServer (def, onCommand) {
281
290
  return { RawColorControlServer, attachLevelTracker }
282
291
  }
283
292
 
293
+ function configureThermostatState (initialState, def, fallbackMode) {
294
+ let hasHeating = def.hasHeatingSetpoint === true
295
+ let hasCooling = def.hasCoolingSetpoint === true
296
+ // The Thermostat cluster cannot be composed without at least one operating feature.
297
+ // Preserve the historical heating default for the standalone Thermostat; a Room Air
298
+ // Conditioner with no setpoint GA configured defaults to cooling, its primary use case.
299
+ if (!hasHeating && !hasCooling) {
300
+ hasHeating = fallbackMode === 'heating'
301
+ hasCooling = fallbackMode === 'cooling'
302
+ }
303
+ const features = []
304
+ if (hasHeating) features.push('Heating')
305
+ if (hasCooling) features.push('Cooling')
306
+ initialState.thermostat = {
307
+ localTemperature: null,
308
+ // CoolingOnly=0, HeatingOnly=2, CoolingAndHeating=4 (ControlSequenceOfOperation).
309
+ controlSequenceOfOperation: hasHeating && hasCooling ? 4 : (hasCooling ? 0 : 2),
310
+ // Cool=3, Heat=4 (SystemMode). A dual-mode device starts in Heat.
311
+ systemMode: hasCooling && !hasHeating ? 3 : 4
312
+ }
313
+ if (hasHeating) initialState.thermostat.occupiedHeatingSetpoint = 2000 // 20°C
314
+ if (hasCooling) initialState.thermostat.occupiedCoolingSetpoint = 2400 // 24°C
315
+ return { features, hasHeating, hasCooling }
316
+ }
317
+
318
+ function attachThermostatCommandHandlers (endpoint, def, onCommand, capabilities) {
319
+ // Absolute thermostat targets are Matter attribute writes, not commands. The offline
320
+ // context marks KNX/flow state synchronization and prevents a feedback write to KNX.
321
+ if (capabilities.hasHeating) {
322
+ endpoint.events.thermostat.occupiedHeatingSetpoint$Changed.on((value, oldValue, context) => {
323
+ try {
324
+ if (context?.offline === true || value === null || value === undefined) return
325
+ onCommand({ deviceId: def.id, fn: 'setpoint', value: Math.round(Number(value)) / 100 })
326
+ } catch (error) { /* empty */ }
327
+ })
328
+ }
329
+ if (capabilities.hasCooling) {
330
+ endpoint.events.thermostat.occupiedCoolingSetpoint$Changed.on((value, oldValue, context) => {
331
+ try {
332
+ if (context?.offline === true || value === null || value === undefined) return
333
+ onCommand({ deviceId: def.id, fn: 'coolingsetpoint', value: Math.round(Number(value)) / 100 })
334
+ } catch (error) { /* empty */ }
335
+ })
336
+ }
337
+ }
338
+
339
+ function configureFanState (initialState) {
340
+ initialState.fanControl = {
341
+ fanMode: 0, // Off
342
+ fanModeSequence: 0, // Off/Low/Med/High (Auto requires the AUT feature)
343
+ percentSetting: 0,
344
+ percentCurrent: 0
345
+ }
346
+ }
347
+
348
+ function attachFanCommandHandlers (endpoint, def, onCommand) {
349
+ endpoint.events.fanControl.percentSetting$Changed.on((value, oldValue, context) => {
350
+ try {
351
+ if (context?.offline === true || value === null || value === undefined) return
352
+ onCommand({ deviceId: def.id, fn: 'fanspeed', value: clamp(Math.round(Number(value)), 0, 100) })
353
+ } catch (error) { /* empty */ }
354
+ })
355
+ endpoint.events.fanControl.fanMode$Changed.on((value, oldValue, context) => {
356
+ try {
357
+ if (context?.offline === true) return
358
+ if (Number(value) === 0) onCommand({ deviceId: def.id, fn: 'fanspeed', value: 0 })
359
+ } catch (error) { /* empty */ }
360
+ })
361
+ }
362
+
284
363
  /**
285
364
  * Creates the bridged Matter endpoint for a virtual device definition.
286
365
  * @param {object} def - { id, type, name }
@@ -353,7 +432,9 @@ function createBridgedEndpoint (def, serialPrefix, onCommand) {
353
432
  return false
354
433
  }
355
434
 
356
- const RawOnOffBase = def.type === 'onoffplug' ? OnOffServer : OnOffServer.with('Lighting')
435
+ const RawOnOffBase = def.type === 'onoffplug'
436
+ ? OnOffServer
437
+ : def.type === 'roomairconditioner' ? OnOffServer.with('DeadFrontBehavior') : OnOffServer.with('Lighting')
357
438
  class RawOnOffServer extends RawOnOffBase {
358
439
  async on () {
359
440
  await super.on()
@@ -505,57 +586,58 @@ function createBridgedEndpoint (def, serialPrefix, onCommand) {
505
586
  // Heating/Cooling capability is auto-detected from which setpoint GA(s) the KNX node
506
587
  // has configured (see knxUltimateMatterBridge.js#getMatterDef). Leaving both flags
507
588
  // unset keeps the original heating-only behavior, so existing flows are unaffected.
508
- const hasCooling = def.hasCoolingSetpoint === true
509
- const hasHeating = def.hasHeatingSetpoint !== false
510
- const features = []
511
- if (hasHeating) features.push('Heating')
512
- if (hasCooling) features.push('Cooling')
513
- if (features.length === 0) features.push('Heating') // never build a thermostat with neither mode
514
-
515
- initialState.thermostat = {
516
- localTemperature: null,
517
- // CoolingOnly=0, HeatingOnly=2, CoolingAndHeating=4 (Matter ControlSequenceOfOperation enum)
518
- controlSequenceOfOperation: hasHeating && hasCooling ? 4 : (hasCooling ? 0 : 2),
519
- // Cool=3, Heat=4 (Matter SystemMode enum). Dual-mode starts in Heat; the controller can
520
- // switch it, same as any physical dual thermostat without an auto/deadband mode.
521
- systemMode: hasCooling && !hasHeating ? 3 : 4
522
- }
523
- if (hasHeating) initialState.thermostat.occupiedHeatingSetpoint = 2000 // 20°C
524
- if (hasCooling) initialState.thermostat.occupiedCoolingSetpoint = 2400 // 24°C
525
-
526
- endpoint = new Endpoint(ThermostatDevice.with(ThermostatServer.with(...features), BridgedDeviceBasicInformationServer), initialState)
527
-
528
- // Setpoint changes from a Matter controller. Unlike OnOff/LevelControl/WindowCovering/
529
- // ColorControl, an absolute setpoint has no cluster COMMAND to intercept - Thermostat
530
- // only exposes SetpointRaiseLower (a *relative* command) and SetActivePresetRequest;
531
- // controllers set an absolute target via a plain attribute write. matter.js only fires
532
- // $Changing/$Changed when the value actually differs (verified in @matter/node's
533
- // Datasource#preCommit), so a duplicate setpoint (same value sent twice) cannot be
534
- // forwarded raw here - there is no lower-level hook to capture it at.
535
- if (hasHeating) {
536
- endpoint.events.thermostat.occupiedHeatingSetpoint$Changed.on((value, oldValue, context) => {
537
- try {
538
- if (context?.offline === true) return
539
- if (value === null || value === undefined) return
540
- onCommand({ deviceId: def.id, fn: 'setpoint', value: Math.round(Number(value)) / 100 }) // centi-°C -> °C
541
- } catch (error) { /* empty */ }
542
- })
543
- }
544
- if (hasCooling) {
545
- endpoint.events.thermostat.occupiedCoolingSetpoint$Changed.on((value, oldValue, context) => {
546
- try {
547
- if (context?.offline === true) return
548
- if (value === null || value === undefined) return
549
- onCommand({ deviceId: def.id, fn: 'coolingsetpoint', value: Math.round(Number(value)) / 100 }) // centi-°C -> °C
550
- } catch (error) { /* empty */ }
551
- })
552
- }
589
+ const thermostat = configureThermostatState(initialState, def, 'heating')
590
+ endpoint = new Endpoint(ThermostatDevice.with(ThermostatServer.with(...thermostat.features), BridgedDeviceBasicInformationServer), initialState)
591
+ attachThermostatCommandHandlers(endpoint, def, onCommand, thermostat)
553
592
  // NOTE: SystemMode (Heat/Cool/Off/Auto) changes from a controller are accepted by the
554
593
  // cluster but not forwarded to KNX yet: there is no single KNX DPT for HVAC mode that
555
594
  // fits every actuator. Track it via the node's optional input PIN if you need it today.
556
595
  return endpoint
557
596
  }
558
597
 
598
+ if (def.type === 'roomairconditioner') {
599
+ // Matter Room Air Conditioner (0x0072) requires OnOff with DeadFrontBehavior and a
600
+ // feature-selected Thermostat server. FanControl is optional in the specification,
601
+ // but included here so the former on/off + thermostat + fan trio becomes one endpoint.
602
+ const thermostat = configureThermostatState(initialState, def, 'cooling')
603
+ configureFanState(initialState)
604
+ endpoint = new Endpoint(RoomAirConditionerDevice.with(
605
+ RawOnOffServer,
606
+ ThermostatServer.with(...thermostat.features),
607
+ FanControlServer,
608
+ BridgedDeviceBasicInformationServer
609
+ ), initialState)
610
+ attachThermostatCommandHandlers(endpoint, def, onCommand, thermostat)
611
+ attachFanCommandHandlers(endpoint, def, onCommand)
612
+ return endpoint
613
+ }
614
+
615
+ if (def.type === 'doorlock') {
616
+ initialState.doorLock = {
617
+ lockState: DoorLock.LockState.Unlocked,
618
+ lockType: DoorLock.LockType.Other,
619
+ actuatorEnabled: true,
620
+ operatingMode: DoorLock.OperatingMode.Normal,
621
+ // matter.js currently defaults these constrained base attributes to 0, which is
622
+ // outside their Matter-defined 1..255 range and prevents endpoint initialization.
623
+ wrongCodeEntryLimit: 3,
624
+ userCodeTemporaryDisableTime: 10
625
+ }
626
+ class KnxDoorLockServer extends DoorLockServer {
627
+ async lockDoor (request) {
628
+ await super.lockDoor(request)
629
+ onCommand({ deviceId: def.id, fn: 'lock', value: true, matterCommand: { cluster: 'DoorLock', command: 'lockDoor' } })
630
+ }
631
+
632
+ async unlockDoor (request) {
633
+ await super.unlockDoor(request)
634
+ onCommand({ deviceId: def.id, fn: 'lock', value: false, matterCommand: { cluster: 'DoorLock', command: 'unlockDoor' } })
635
+ }
636
+ }
637
+ endpoint = new Endpoint(DoorLockDevice.with(KnxDoorLockServer, BridgedDeviceBasicInformationServer), initialState)
638
+ return endpoint
639
+ }
640
+
559
641
  if (def.type === 'occupancysensor') {
560
642
  // The OccupancySensing cluster is not added by default: the sensor type must be chosen (we expose it as PIR)
561
643
  endpoint = new Endpoint(OccupancySensorDevice.with(OccupancySensingServer.with('PassiveInfrared'), BridgedDeviceBasicInformationServer), initialState)
@@ -620,30 +702,13 @@ function createBridgedEndpoint (def, serialPrefix, onCommand) {
620
702
  BridgedDeviceBasicInformationServer
621
703
  ), initialState)
622
704
  } else if (def.type === 'fan') {
623
- initialState.fanControl = {
624
- fanMode: 0, // Off
625
- fanModeSequence: 0, // Off/Low/Med/High (Auto requires the AUT feature)
626
- percentSetting: 0,
627
- percentCurrent: 0
628
- }
705
+ configureFanState(initialState)
629
706
  endpoint = new Endpoint(FanDevice.with(BridgedDeviceBasicInformationServer), initialState)
630
707
  // Speed changes from the controller (percent write or fan mode change). Like the
631
708
  // Thermostat setpoints above, FanControlServer exposes no command to intercept for
632
709
  // percentSetting (@matter/node's FanControlServer overrides nothing beyond
633
710
  // initialize()) - it is attribute-write only, so this cannot be made "raw" either.
634
- endpoint.events.fanControl.percentSetting$Changed.on((value, oldValue, context) => {
635
- try {
636
- if (context?.offline === true) return
637
- if (value === null || value === undefined) return
638
- onCommand({ deviceId: def.id, fn: 'fanspeed', value: clamp(Math.round(Number(value)), 0, 100) })
639
- } catch (error) { /* empty */ }
640
- })
641
- endpoint.events.fanControl.fanMode$Changed.on((value, oldValue, context) => {
642
- try {
643
- if (context?.offline === true) return
644
- if (Number(value) === 0) onCommand({ deviceId: def.id, fn: 'fanspeed', value: 0 })
645
- } catch (error) { /* empty */ }
646
- })
711
+ attachFanCommandHandlers(endpoint, def, onCommand)
647
712
  } else if (def.type === 'robotvacuum') {
648
713
  // Flow-only device: run mode changes and operational commands are forwarded to the flow
649
714
  // (node PINs); the flow reports the state back through the input PIN.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=20.18.1"
5
5
  },
6
- "version": "6.3.13",
6
+ "version": "6.3.14",
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/",