node-red-contrib-knx-ultimate 6.0.4 → 6.0.5
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 +12 -0
- package/nodes/knxUltimateMatterBridge.html +38 -15
- package/nodes/knxUltimateMatterBridge.js +70 -15
- package/nodes/locales/de/knxUltimateMatterBridge.html +2 -2
- package/nodes/locales/de/knxUltimateMatterBridge.json +13 -10
- package/nodes/locales/en/knxUltimateMatterBridge.html +2 -2
- package/nodes/locales/en/knxUltimateMatterBridge.json +13 -10
- package/nodes/locales/es/knxUltimateMatterBridge.html +2 -2
- package/nodes/locales/es/knxUltimateMatterBridge.json +13 -10
- package/nodes/locales/fr/knxUltimateMatterBridge.html +2 -2
- package/nodes/locales/fr/knxUltimateMatterBridge.json +13 -10
- package/nodes/locales/it/knxUltimateMatterBridge.html +2 -2
- package/nodes/locales/it/knxUltimateMatterBridge.json +13 -10
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.html +2 -2
- package/nodes/locales/zh-CN/knxUltimateMatterBridge.json +13 -10
- package/nodes/utils/matterBridgeDeviceFactory.mjs +244 -130
- package/nodes/utils/matterBridgeEngine.mjs +27 -22
- package/nodes/utils/matterEngine.mjs +13 -7
- package/nodes/utils/matterEnvironmentLock.mjs +32 -0
- package/package.json +1 -1
|
@@ -71,27 +71,31 @@ class classMatterBridge extends EventEmitter {
|
|
|
71
71
|
try {
|
|
72
72
|
api.Logger.level = api.LogLevel.ERROR
|
|
73
73
|
} catch (error) { /* empty */ }
|
|
74
|
-
const
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
this.server = await
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
74
|
+
const { withEnvironmentLock } = await import('./matterEnvironmentLock.mjs')
|
|
75
|
+
// Environment.default (and its 'storage.path' var) is a process-wide singleton shared
|
|
76
|
+
// with the Matter Controller engine: serialize the set+create critical section so a
|
|
77
|
+
// concurrent startup can never observe another engine's storage path mid-flight.
|
|
78
|
+
this.server = await withEnvironmentLock(async () => {
|
|
79
|
+
const environment = api.Environment.default
|
|
80
|
+
environment.vars.set('storage.path', this.storagePath)
|
|
81
|
+
return api.ServerNode.create({
|
|
82
|
+
id: this.instanceId,
|
|
83
|
+
network: { port: this.options.port },
|
|
84
|
+
productDescription: {
|
|
85
|
+
name: this.options.deviceName,
|
|
86
|
+
deviceType: api.AggregatorEndpoint.deviceType
|
|
87
|
+
},
|
|
88
|
+
basicInformation: {
|
|
89
|
+
vendorName: 'KNX-Ultimate',
|
|
90
|
+
vendorId: api.VendorId(0xFFF1), // Test vendor ID
|
|
91
|
+
nodeLabel: this.options.deviceName,
|
|
92
|
+
productName: this.options.deviceName,
|
|
93
|
+
productLabel: this.options.deviceName,
|
|
94
|
+
productId: 0x8000,
|
|
95
|
+
serialNumber: `knxu-${this.instanceId}`.slice(0, 32),
|
|
96
|
+
uniqueId: this.instanceId.slice(-32)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
95
99
|
})
|
|
96
100
|
|
|
97
101
|
this.aggregator = new api.Endpoint(api.AggregatorEndpoint, { id: 'aggregator' })
|
|
@@ -171,7 +175,8 @@ class classMatterBridge extends EventEmitter {
|
|
|
171
175
|
const needsRecreate = newDef !== undefined && oldDef !== undefined &&
|
|
172
176
|
(newDef.type !== oldDef.type ||
|
|
173
177
|
(newDef.invertPosition === true) !== (oldDef.invertPosition === true) ||
|
|
174
|
-
(newDef.
|
|
178
|
+
(newDef.hasHeatingSetpoint !== false) !== (oldDef.hasHeatingSetpoint !== false) ||
|
|
179
|
+
(newDef.hasCoolingSetpoint === true) !== (oldDef.hasCoolingSetpoint === true))
|
|
175
180
|
if (newDef === undefined || needsRecreate) {
|
|
176
181
|
await this.removeDeviceEndpoint(id)
|
|
177
182
|
}
|
|
@@ -175,14 +175,20 @@ class classMatter extends EventEmitter {
|
|
|
175
175
|
// Quiet down the very verbose default matter.js console logging.
|
|
176
176
|
api.Logger.level = api.LogLevel.ERROR
|
|
177
177
|
} catch (error) { /* empty */ }
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
178
|
+
const { withEnvironmentLock } = await import('./matterEnvironmentLock.mjs')
|
|
179
|
+
// Serialize with the Matter Bridge engine: both write Environment.default.vars
|
|
180
|
+
// ('storage.path') before creating their matter.js node, and that Environment is a
|
|
181
|
+
// process-wide singleton. See matterEnvironmentLock.mjs for why this matters.
|
|
182
|
+
await withEnvironmentLock(async () => {
|
|
183
|
+
const environment = api.Environment.default
|
|
184
|
+
environment.vars.set('storage.path', this.storagePath)
|
|
185
|
+
this.controller = new api.CommissioningController({
|
|
186
|
+
environment: { environment, id: this.instanceId },
|
|
187
|
+
autoConnect: false,
|
|
188
|
+
adminFabricLabel: this.fabricLabel
|
|
189
|
+
})
|
|
190
|
+
await this.controller.start()
|
|
184
191
|
})
|
|
185
|
-
await this.controller.start()
|
|
186
192
|
this.matterConnectionStatus = 'connected'
|
|
187
193
|
this.emit('connected')
|
|
188
194
|
// Connect all already commissioned nodes and hook their events.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
// matter.js keeps its runtime configuration (including 'storage.path') on
|
|
3
|
+
// Environment.default, a SINGLE process-wide object shared by every ServerNode /
|
|
4
|
+
// CommissioningController instance (the KNX Matter Bridge engine AND the Matter
|
|
5
|
+
// Controller engine both read/write it). Today every engine instance in this
|
|
6
|
+
// package happens to set the same 'storage.path' value, so the two engines
|
|
7
|
+
// racing on Environment.default.vars.set() before their own create() call has
|
|
8
|
+
// historically been harmless in practice. It is still a shared-mutable-global
|
|
9
|
+
// access pattern: if a future change ever gives two engines different paths
|
|
10
|
+
// (e.g. per-instance storage isolation), an unguarded interleaving could make
|
|
11
|
+
// engine A's ServerNode.create() observe engine B's path.
|
|
12
|
+
//
|
|
13
|
+
// This module offers a tiny FIFO async mutex so callers can wrap
|
|
14
|
+
// "set the env var(s) then create the node" as one atomic critical section,
|
|
15
|
+
// regardless of how many bridge/controller config nodes start concurrently.
|
|
16
|
+
let queue = Promise.resolve()
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Runs `fn` exclusively with respect to every other call to withEnvironmentLock
|
|
20
|
+
* in this process. Calls are queued FIFO; a throwing/rejecting `fn` does not
|
|
21
|
+
* block subsequent queued calls.
|
|
22
|
+
* @param {() => Promise<any>} fn
|
|
23
|
+
* @returns {Promise<any>} whatever `fn` resolves to
|
|
24
|
+
*/
|
|
25
|
+
function withEnvironmentLock (fn) {
|
|
26
|
+
const run = queue.then(fn, fn)
|
|
27
|
+
// Keep the chain alive even if `fn` rejects, so later callers still run.
|
|
28
|
+
queue = run.then(() => undefined, () => undefined)
|
|
29
|
+
return run
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { withEnvironmentLock }
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=20.18.1"
|
|
5
5
|
},
|
|
6
|
-
"version": "6.0.
|
|
6
|
+
"version": "6.0.5",
|
|
7
7
|
"description": "Control your KNX and KNX Secure intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control, ETS group address importer, KNX AI for diagnosticsand KNX routing between interfaces. Easy to use and highly configurable.",
|
|
8
8
|
"files": [
|
|
9
9
|
"nodes/",
|