loupedeck-commander 1.2.12 → 1.3.1

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,27 +2,14 @@ import {
2
2
  OPCUAClient,
3
3
  MessageSecurityMode,
4
4
  SecurityPolicy,
5
- BrowseDirection,
6
5
  AttributeIds,
7
- NodeClassMask,
8
- makeBrowsePath,
9
6
  resolveNodeId,
10
7
  TimestampsToReturn,
11
- coerceInt32,
12
- coerceByteString,
13
8
  DataType
14
9
  } from "node-opcua";
15
10
 
16
11
  import { BaseIf } from './baseif.mjs'
17
12
 
18
- const subscriptionParameters = {
19
- maxNotificationsPerPublish: 1000,
20
- publishingEnabled: true,
21
- requestedLifetimeCount: 100,
22
- requestedMaxKeepAliveCount: 10,
23
- requestedPublishingInterval: 1000
24
- };
25
-
26
13
  /**
27
14
  * Our Special-Handler just used the Default - and adds Vibration after triggers through Button-Releases
28
15
  */
@@ -34,19 +21,13 @@ export class OPCUAIf extends BaseIf {
34
21
  #connected
35
22
  #endpointurl
36
23
  #publishingInterval
24
+ #samplingInterval
37
25
  monitoreditems
38
26
  types
39
27
  buttons
40
28
  constructor() {
41
29
  super()
42
- }
43
-
44
- async stop() {
45
- if (!this.#client)
46
- return
47
- await this.Disconnect()
48
- this.LogInfo(`OPCUAIf Stopped\n`)
49
- }
30
+ }
50
31
 
51
32
  /**
52
33
  * Initialize the OPCUA client and subscribe to monitored items.
@@ -67,6 +48,12 @@ export class OPCUAIf extends BaseIf {
67
48
  this.#publishingInterval = 250;
68
49
  this.LogInfo(`OPCUAIf init using default publishingInterval: ${this.#publishingInterval}ms\n`);
69
50
  }
51
+ if (options.samplingInterval)
52
+ this.#samplingInterval = options.samplingInterval
53
+ else{
54
+ this.#samplingInterval = 100;
55
+ this.LogInfo(`OPCUAIf init using default samplingInterval: ${this.#samplingInterval}ms\n`);
56
+ }
70
57
  this.monitoreditems = {}
71
58
  this.types = {}
72
59
  this.buttons = {}
@@ -79,33 +66,33 @@ export class OPCUAIf extends BaseIf {
79
66
  for (let f = 0; f < fieldKeys.length; f++) {
80
67
  let field = fields[f]
81
68
  const keys = Object.keys(field)
69
+
70
+ // Iterate over buttons:
82
71
  for (let i = 0; i < keys.length; i++) {
83
72
  const key = keys[i]
84
73
  const elem = field[key]
74
+ options["key"] = key // we have to know the key of the button
85
75
  // groupnode
86
- if (elem.nodeid) {
87
- options["key"] = key
88
- let formattedNodeId = this.formatString(elem.nodeid, options)
76
+ if (elem.params && elem.params.nodeid) {
77
+ let formattedNodeId = this.formatString(elem.params.nodeid, options)
89
78
  let monitoredItemId = await this.Subscribe(formattedNodeId)
90
- console.log("Subscribe to",formattedNodeId)
91
- this.buttons[monitoredItemId] = i
92
- }
93
- // statenode
94
- if (elem.statenodeid) {
95
- let format = this.formatString(elem.statenodeid, options)
96
- let monitoredItemId = await this.Subscribe(format)
79
+ if (monitoredItemId === undefined) {
80
+ this.LogError(`OPCUAIf: Error subscribing to node ${formattedNodeId}\n`)
81
+ continue
82
+ }
83
+ console.log("OPCUAIf: Subscribe to",monitoredItemId,formattedNodeId)
97
84
  this.buttons[monitoredItemId] = i
98
85
  }
99
86
  await this.monitorStates(elem, options)
100
87
  }
101
88
  }
102
89
  } catch (error) {
103
- this.LogError(`OPCUAIf: Error $error\n`)
90
+ this.LogError(`OPCUAIf: Error\n`,error)
104
91
  }
105
92
  }
106
93
 
107
94
  /**
108
- *
95
+ * Monitor the state OPC/UA node of the given element
109
96
  * @param {*} elem : Elem Object
110
97
  * @param {*} options
111
98
  */
@@ -115,8 +102,13 @@ export class OPCUAIf extends BaseIf {
115
102
  const key2 = stateKeys[i]
116
103
  const state = elem.states[key2]
117
104
  if (state.opcua) {
118
- let format = this.formatString(state.opcua, options)
119
- let monitoredItemId = await this.Subscribe(format)
105
+ let formattedNodeId = this.formatString(state.opcua, options)
106
+ let monitoredItemId = await this.Subscribe(formattedNodeId)
107
+ if (monitoredItemId === undefined) {
108
+ this.LogError(`OPCUAIf: Error subscribing State to node ${formattedNodeId}\n`)
109
+ continue
110
+ }
111
+ console.log("OPCUAIf: Subscribe to",monitoredItemId,formattedNodeId)
120
112
  this.buttons[monitoredItemId] = i
121
113
  }
122
114
  }
@@ -167,6 +159,13 @@ export class OPCUAIf extends BaseIf {
167
159
  }
168
160
  }
169
161
 
162
+ /**
163
+ * Call the OPCUA node with the given options.
164
+ * This method will write the value to the node and return the new state.
165
+ * @param {*} opcuaNode
166
+ * @param {*} options
167
+ * @returns
168
+ */
170
169
  async call(opcuaNode, options = {}) {
171
170
  var res = this.Check(options)
172
171
  if (res < 0) {
@@ -174,7 +173,7 @@ export class OPCUAIf extends BaseIf {
174
173
  return false
175
174
  }
176
175
 
177
- var nodeId = super.formatString(opcuaNode, options)
176
+ var nodeId = super.call(opcuaNode, options)
178
177
 
179
178
  var type = this.types[nodeId]
180
179
  var value = options.value
@@ -214,6 +213,21 @@ export class OPCUAIf extends BaseIf {
214
213
  return 0
215
214
  }
216
215
 
216
+ /**
217
+ * This method is called when the interface is stopped.
218
+ * It will disconnect the client and stop the subscription.
219
+ * @returns
220
+ */
221
+ async stop() {
222
+ if (!this.#client)
223
+ return
224
+ await this.Disconnect()
225
+ this.LogInfo(`OPCUAIf Stopped\n`)
226
+ }
227
+
228
+ /**
229
+ * Disconnect the OPCUA client and close the session.
230
+ */
217
231
  async Disconnect() {
218
232
  if (this.#client) {
219
233
  if (this.#session)
@@ -223,6 +237,10 @@ export class OPCUAIf extends BaseIf {
223
237
  this.LogInfo(`OPCUAIf: Disconnected\n`);
224
238
  }
225
239
  }
240
+ /**
241
+ * Connect to the OPCUA server with the given URL.
242
+ * @param {string} url - The URL of the OPCUA server, typical format: opc.tcp://ip-address:4840 .
243
+ */
226
244
  async Connect(url) {
227
245
  let self = this
228
246
  if (this.#client){
@@ -270,9 +288,9 @@ export class OPCUAIf extends BaseIf {
270
288
  this.#client.on("after_reconnection", (err) => {
271
289
  self.LogInfo(`OPCUAIf: After Reconnection event => ${err}\n`);
272
290
  });
273
- this.#client.on("security_token_renewed", () => {
291
+ /*this.#client.on("security_token_renewed", () => {
274
292
  self.LogDebug(`OPCUAIf: security_token_renewed\n`);
275
- })
293
+ })*/
276
294
  this.#client.on("lifetime_75", (token) => { })
277
295
 
278
296
  this.LogInfo(`OPCUAIf: connecting client to ${url}\n`);//, this.#session.toString());
@@ -302,16 +320,22 @@ export class OPCUAIf extends BaseIf {
302
320
  requestedPublishingInterval: this.#publishingInterval
303
321
  });
304
322
 
305
- this.LogInfo(`OPCUAIf: session created\n`);//, this.#session.toString());
306
- this.LogInfo(`OPCUAIf: client\n`);
307
- this.LogInfo(`OPCUAIf: subscription\n`);
323
+ this.LogInfo(`OPCUAIf: session created\n`);
324
+ // this.LogInfo(`OPCUAIf: client\n`);
325
+ // this.LogInfo(`OPCUAIf: subscription\n`);
308
326
  this.#connected = true
309
327
  this.#endpointurl = url
310
328
  }
311
329
 
330
+ /**
331
+ * Register a subscription for a nodeID.
332
+ * This method will create a monitored item for the given nodeID and return the monitored item ID.
333
+ * If the nodeID is already registered, it will return the existing monitored item ID.
334
+ * @param {*} nodeID
335
+ * @returns
336
+ */
312
337
  async Subscribe(nodeID) {
313
338
  // install monitored item
314
-
315
339
  const keys = Object.keys(this.monitoreditems)
316
340
  for (let i = 0; i < keys.length; i++) {
317
341
  const key = keys[i]
@@ -327,7 +351,7 @@ export class OPCUAIf extends BaseIf {
327
351
  attributeId: AttributeIds.Value
328
352
  };
329
353
  const monitoringParameters = {
330
- samplingInterval: 100,
354
+ samplingInterval: this.#samplingInterval,
331
355
  discardOldest: true,
332
356
  queueSize: 10
333
357
  };
@@ -393,12 +417,10 @@ export class OPCUAIf extends BaseIf {
393
417
  }
394
418
  } else {
395
419
  self.LogError("OPCUAIf: write NOT ok", nodeID, value, "\n");
396
- self.LogError(err)
397
420
  }
398
421
  });
399
422
  } catch (err) {
400
423
  self.LogError("OPCUAIf: write NOT ok", nodeID, value, "\n");
401
- self.LogError(err)
402
424
  }
403
425
  }
404
426
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loupedeck-commander",
3
- "version": "1.2.12",
3
+ "version": "1.3.1",
4
4
  "description": "A system to ease working with LoupeDeck devices using CMD-line, OPC/UA or HTTP-client interfaces",
5
5
  "main": "index.mjs",
6
6
  "scripts": {
@@ -14,7 +14,8 @@
14
14
  "loupedeck": "^7.0.0",
15
15
  "mkdirp": "^3.0.1",
16
16
  "node-opcua": "^2.153.0",
17
- "string-template": "^1.0.0"
17
+ "string-template": "^1.0.0",
18
+ "yaml": "^2.8.0"
18
19
  },
19
20
  "author": "Thomas Schneider",
20
21
  "license": "Apache-2.0",
package/profile-1.yaml ADDED
@@ -0,0 +1,185 @@
1
+ name: profile-1
2
+ profile: example
3
+ description: ""
4
+ touch:
5
+ center:
6
+ "0":
7
+ states:
8
+ off:
9
+ color: "#000055"
10
+ textColor: "#FFFFFF"
11
+ image: icons/home.png
12
+ cmd: echo "{id} {state}"
13
+ font: 20px Arial
14
+ blink: true
15
+ on:
16
+ color: "#00ff00"
17
+ image: icons/home.png
18
+ cmd: echo "{id} {state}"
19
+ font: 10px Arial
20
+ blink: false
21
+ params:
22
+ text: "{key}"
23
+ "1":
24
+ states:
25
+ off:
26
+ color: "#000099"
27
+ image: icons/home.png
28
+ cmd: echo "{id} {state} {pressed}"
29
+ on:
30
+ color: "#00ff00"
31
+ image: icons/home.png
32
+ cmd: echo "{id} {state} {pressed}"
33
+ blink: true
34
+ params:
35
+ minPressed: 100
36
+ text: "{released}"
37
+ "2":
38
+ states:
39
+ off:
40
+ color: "#000099"
41
+ image: icons/home.png
42
+ cmd: echo "{id} {state}"
43
+ on:
44
+ color: "#00ff00"
45
+ image: icons/home.png
46
+ cmd: echo "{id} {state}"
47
+ "3":
48
+ states:
49
+ off:
50
+ color: "#000099"
51
+ image: icons/home.png
52
+ cmd: echo "{id} {state}"
53
+ on:
54
+ color: "#00ff00"
55
+ image: icons/home.png
56
+ cmd: echo "{id} {state}"
57
+ "4":
58
+ states:
59
+ off:
60
+ color: "#000099"
61
+ image: icons/home.png
62
+ cmd: echo "{id} {state}"
63
+ on:
64
+ color: "#00ff00"
65
+ image: icons/home.png
66
+ cmd: echo "{id} {state}"
67
+ "5":
68
+ states:
69
+ off:
70
+ color: "#000099"
71
+ image: icons/home.png
72
+ cmd: echo "{id} {state}"
73
+ on:
74
+ color: "#00ff00"
75
+ image: icons/home.png
76
+ cmd: echo "{id} {state}"
77
+ "6":
78
+ states:
79
+ off:
80
+ color: "#000099"
81
+ image: icons/home.png
82
+ cmd: echo "{id} {state}"
83
+ on:
84
+ color: "#00ff00"
85
+ image: icons/home.png
86
+ cmd: echo "{id} {state}"
87
+ "7":
88
+ states:
89
+ off:
90
+ color: "#000099"
91
+ image: icons/home.png
92
+ cmd: echo "{id} {state}"
93
+ on:
94
+ color: "#00ff00"
95
+ image: icons/home.png
96
+ cmd: echo "{id} {state}"
97
+ "8":
98
+ states:
99
+ off:
100
+ color: "#000099"
101
+ image: icons/home.png
102
+ cmd: echo "{id} {state}"
103
+ on:
104
+ color: "#00ff00"
105
+ image: icons/home.png
106
+ cmd: echo "{id} {state}"
107
+ "9":
108
+ states:
109
+ off:
110
+ color: "#000099"
111
+ image: icons/home.png
112
+ cmd: echo "{id} {state}"
113
+ on:
114
+ color: "#00ff00"
115
+ image: icons/home.png
116
+ cmd: echo "{id} {state}"
117
+ "10":
118
+ states:
119
+ off:
120
+ color: "#000099"
121
+ image: icons/home.png
122
+ cmd: echo "{id} {state}"
123
+ on:
124
+ color: "#00ff00"
125
+ image: icons/home.png
126
+ cmd: echo "{id} {state}"
127
+ "11":
128
+ states:
129
+ off:
130
+ color: "#000099"
131
+ image: icons/home.png
132
+ cmd: echo "{id} {state}"
133
+ on:
134
+ color: "#00ff00"
135
+ image: icons/home.png
136
+ cmd: echo "{id} {state}"
137
+ left:
138
+ "0":
139
+ states:
140
+ on:
141
+ color: "#000000"
142
+ cmd: echo "{id} {state}"
143
+ right:
144
+ "0":
145
+ states:
146
+ on:
147
+ color: "#000000"
148
+ cmd: echo "{id} {state}"
149
+ knobs:
150
+ knobTL:
151
+ states:
152
+ on:
153
+ cmd: echo "{id} {state}"
154
+ knobCL:
155
+ states:
156
+ on:
157
+ cmd: echo "{id} {state}"
158
+ knobBL:
159
+ states:
160
+ on:
161
+ cmd: echo "{id} {state}"
162
+ opcua: ns=1;s={simnbr}.Function1
163
+ knobTR:
164
+ states:
165
+ on:
166
+ cmd: echo "{id} {state}"
167
+ knobCR:
168
+ states:
169
+ on:
170
+ cmd: echo "{id} {state}"
171
+ knobBR:
172
+ states:
173
+ on:
174
+ cmd: echo "{id} {state}"
175
+ parameters:
176
+ hostname: 127.0.0.1
177
+ simnbr: "1"
178
+ endpointurl: opc.tcp://{hostname}:4840
179
+ publishingInterval: 200
180
+ nodeid: ns=0;s=nodeID
181
+ textColor: "#abcdff"
182
+ textBaseLine: top
183
+ textAlign: left
184
+ font: 14px Arial
185
+ verbose: true
package/profile-2.yaml ADDED
@@ -0,0 +1,210 @@
1
+ name: profile-1
2
+ profile: example
3
+ description: ""
4
+ touch:
5
+ center:
6
+ "0":
7
+ default: one
8
+ states:
9
+ off:
10
+ color: "#000099"
11
+ cmd: echo "{id} {state}"
12
+ one:
13
+ color: "#00ff00"
14
+ image: numbers/one_9278045.png
15
+ cmd: echo "{id} {state}"
16
+ two:
17
+ color: "#00ff00"
18
+ image: numbers/two_9278103.png
19
+ cmd: echo "{id} {state}"
20
+ three:
21
+ color: "#00ff00"
22
+ image: numbers/three_9278150.png
23
+ cmd: echo "{id} {state}"
24
+ four:
25
+ color: "#00ff00"
26
+ image: numbers/four_9278183.png
27
+ cmd: echo "{id} {state}"
28
+ five:
29
+ color: "#00ff00"
30
+ image: numbers/five_9278222.png
31
+ cmd: echo "{id} {state}"
32
+ "1":
33
+ default: two
34
+ states:
35
+ off:
36
+ color: "#000099"
37
+ cmd: echo "{id} {state}"
38
+ one:
39
+ color: "#00ff00"
40
+ image: numbers/one_9278045.png
41
+ cmd: echo "{id} {state}"
42
+ two:
43
+ color: "#00ff00"
44
+ image: numbers/two_9278103.png
45
+ cmd: echo "{id} {state}"
46
+ three:
47
+ color: "#00ff00"
48
+ image: numbers/three_9278150.png
49
+ cmd: echo "{id} {state}"
50
+ four:
51
+ color: "#00ff00"
52
+ image: numbers/four_9278183.png
53
+ cmd: echo "{id} {state}"
54
+ five:
55
+ color: "#00ff00"
56
+ image: numbers/five_9278222.png
57
+ cmd: echo "{id} {state}"
58
+ "2":
59
+ default: three
60
+ states:
61
+ off:
62
+ color: "#000099"
63
+ cmd: echo "{id} {state}"
64
+ one:
65
+ color: "#00ff00"
66
+ image: numbers/one_9278045.png
67
+ cmd: echo "{id} {state}"
68
+ two:
69
+ color: "#00ff00"
70
+ image: numbers/two_9278103.png
71
+ cmd: echo "{id} {state}"
72
+ three:
73
+ color: "#00ff00"
74
+ image: numbers/three_9278150.png
75
+ cmd: echo "{id} {state}"
76
+ four:
77
+ color: "#00ff00"
78
+ image: numbers/four_9278183.png
79
+ cmd: echo "{id} {state}"
80
+ five:
81
+ color: "#00ff00"
82
+ image: numbers/five_9278222.png
83
+ cmd: echo "{id} {state}"
84
+ "3":
85
+ default: four
86
+ states:
87
+ off:
88
+ color: "#000099"
89
+ cmd: echo "{id} {state}"
90
+ one:
91
+ color: "#00ff00"
92
+ image: numbers/one_9278045.png
93
+ cmd: echo "{id} {state}"
94
+ two:
95
+ color: "#00ff00"
96
+ image: numbers/two_9278103.png
97
+ cmd: echo "{id} {state}"
98
+ three:
99
+ color: "#00ff00"
100
+ image: numbers/three_9278150.png
101
+ cmd: echo "{id} {state}"
102
+ four:
103
+ color: "#00ff00"
104
+ image: numbers/four_9278183.png
105
+ cmd: echo "{id} {state}"
106
+ five:
107
+ color: "#00ff00"
108
+ image: numbers/five_9278222.png
109
+ cmd: echo "{id} {state}"
110
+ "4":
111
+ default: five
112
+ states:
113
+ off:
114
+ color: "#000099"
115
+ cmd: echo "{id} {state}"
116
+ one:
117
+ color: "#00ff00"
118
+ image: numbers/one_9278045.png
119
+ cmd: echo "{id} {state}"
120
+ two:
121
+ color: "#00ff00"
122
+ image: numbers/two_9278103.png
123
+ cmd: echo "{id} {state}"
124
+ three:
125
+ color: "#00ff00"
126
+ image: numbers/three_9278150.png
127
+ cmd: echo "{id} {state}"
128
+ four:
129
+ color: "#00ff00"
130
+ image: numbers/four_9278183.png
131
+ cmd: echo "{id} {state}"
132
+ five:
133
+ color: "#00ff00"
134
+ image: numbers/five_9278222.png
135
+ cmd: echo "{id} {state}"
136
+ "5":
137
+ states:
138
+ off:
139
+ color: "#000099"
140
+ cmd: echo "{id} {state}"
141
+ one:
142
+ color: "#00ff00"
143
+ image: numbers/one_9278045.png
144
+ cmd: echo "{id} {state}"
145
+ two:
146
+ color: "#00ff00"
147
+ image: numbers/two_9278103.png
148
+ cmd: echo "{id} {state}"
149
+ three:
150
+ color: "#00ff00"
151
+ image: numbers/three_9278150.png
152
+ cmd: echo "{id} {state}"
153
+ four:
154
+ color: "#00ff00"
155
+ image: numbers/four_9278183.png
156
+ cmd: echo "{id} {state}"
157
+ five:
158
+ color: "#00ff00"
159
+ image: numbers/five_9278222.png
160
+ cmd: echo "{id} {state}"
161
+ left:
162
+ "0":
163
+ states:
164
+ on:
165
+ color: "#000000"
166
+ cmd: echo "{id} {state}"
167
+ right:
168
+ "0":
169
+ states:
170
+ on:
171
+ color: "#000000"
172
+ cmd: echo "{id} {state}"
173
+ knobs:
174
+ knobTL:
175
+ states:
176
+ on:
177
+ cmd: echo "{id} {state}"
178
+ group: ""
179
+ knobCL:
180
+ states:
181
+ on:
182
+ cmd: echo "{id} {state}"
183
+ group: ""
184
+ knobBL:
185
+ states:
186
+ on:
187
+ cmd: echo "{id} {state}"
188
+ opcua: ns=2;s=Is{simnbr}.Audio.in.VolumeAccustic
189
+ group: ""
190
+ knobTR:
191
+ states:
192
+ on:
193
+ cmd: echo "{id} {state}"
194
+ group: ""
195
+ knobCR:
196
+ states:
197
+ on:
198
+ cmd: echo "{id} {state}"
199
+ group: ""
200
+ knobBR:
201
+ states:
202
+ on:
203
+ cmd: echo "{id} {state}"
204
+ group: ""
205
+ parameters:
206
+ hostname: 127.0.0.1
207
+ simnbr: "1"
208
+ endpointurl: opc.tcp://{hostname}:4840
209
+ nodeid: ns=0;s=nodeID
210
+ verbose: true
package/test.mjs CHANGED
@@ -1,6 +1,20 @@
1
1
  import { BaseLoupeDeckHandler } from './common/BaseLoupeDeckHandler.mjs'
2
2
 
3
- var config='config.json'
3
+ var config='config.yaml'
4
+
5
+ // Check if a config file exists
6
+ import { existsSync } from 'fs'
7
+ if (existsSync(config)) {
8
+ console.log(`Using config file: ${config}`)
9
+ } else if (existsSync(config.replace('.yaml', '.json'))) {
10
+ // If a JSON config file exists, use it instead
11
+ config = config.replace('.yaml', '.json')
12
+ console.log(`Using config file: ${config}`)
13
+ } else {
14
+ // If no config file exists, exit with an error message
15
+ console.error(`Config file ${config} does not exist. Please create it.`)
16
+ process.exit(1)
17
+ }
4
18
 
5
19
  /**
6
20
  * Initialize the Handler using a default config-File