loupedeck-commander 1.3.0 → 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.
package/common/utils.mjs CHANGED
@@ -1,17 +1,29 @@
1
1
  import { readFileSync, writeFileSync } from 'node:fs'
2
+ import YAML from 'yaml'
2
3
 
3
4
  /**
4
- * Read a JSON File
5
+ * Read a Config file in either JSON or YAML Fileformat
6
+ * @param {string} fileName - The name of the file to read
7
+ * @returns {object} - The parsed object from the file
5
8
  */
6
- export function readJSONFile (fileName) {
7
- let data
9
+ export function readConfigFile (fileName) {
10
+ let obj = undefined
8
11
  try {
9
- data = readFileSync(fileName, 'utf8')
10
- return JSON.parse(data)
12
+ //console.log("Reading File:", fileName)
13
+ let data = readFileSync(fileName, 'utf8')
14
+ // If the file is a YAML file, parse it
15
+ if (fileName.endsWith('.yaml') || fileName.endsWith('.yml')) {
16
+ obj = YAML.parse(data)
17
+ } else if (fileName.endsWith('.json')) {
18
+ // If the file is a JSON file, parse it
19
+ obj = JSON.parse(data)
20
+ // automatically convert all json to yaml
21
+ writeYAMLFile(fileName.replace(".json",".yaml"), obj)
22
+ }
11
23
  } catch (error) {
12
- console.info(`Error reading File: ${fileName}`)
13
- return data
24
+ console.info(`Error reading File: ${fileName}`, error)
14
25
  }
26
+ return obj
15
27
  }
16
28
 
17
29
  /**
@@ -22,6 +34,15 @@ export function writeJSONFile (fileName, jsonObj) {
22
34
  writeFileSync(fileName, data)
23
35
  }
24
36
 
37
+ /**
38
+ * Write a JSON File
39
+ */
40
+ export function writeYAMLFile (fileName, jsonObj) {
41
+ const data = YAML.stringify(jsonObj)
42
+ writeFileSync(fileName, data)
43
+ }
44
+
45
+
25
46
  /**
26
47
  * Calculate the delta of a value within a range between min and max with overflow
27
48
  * @param {*} data
package/config.yaml ADDED
@@ -0,0 +1,6 @@
1
+ application: Example
2
+ profiles:
3
+ - name: profile-1
4
+ file: profile-1.yaml
5
+ - name: profile-2
6
+ file: profile-2.yaml
@@ -57,7 +57,7 @@ export class BaseIf extends EventEmitter {
57
57
 
58
58
  LogDebug(...args){
59
59
  if (this.options && this.options.verbose){
60
- let str = new String(args)
60
+ let str = args.join(' ')
61
61
  process.stdout.write(str.toString())
62
62
  }
63
63
  }
@@ -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 = {}
@@ -89,7 +76,11 @@ export class OPCUAIf extends BaseIf {
89
76
  if (elem.params && elem.params.nodeid) {
90
77
  let formattedNodeId = this.formatString(elem.params.nodeid, options)
91
78
  let monitoredItemId = await this.Subscribe(formattedNodeId)
92
- console.log("Subscribe to",monitoredItemId,formattedNodeId)
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)
93
84
  this.buttons[monitoredItemId] = i
94
85
  }
95
86
  await this.monitorStates(elem, options)
@@ -101,7 +92,7 @@ export class OPCUAIf extends BaseIf {
101
92
  }
102
93
 
103
94
  /**
104
- *
95
+ * Monitor the state OPC/UA node of the given element
105
96
  * @param {*} elem : Elem Object
106
97
  * @param {*} options
107
98
  */
@@ -113,7 +104,11 @@ export class OPCUAIf extends BaseIf {
113
104
  if (state.opcua) {
114
105
  let formattedNodeId = this.formatString(state.opcua, options)
115
106
  let monitoredItemId = await this.Subscribe(formattedNodeId)
116
- console.log("Subscribe to",monitoredItemId,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)
117
112
  this.buttons[monitoredItemId] = i
118
113
  }
119
114
  }
@@ -164,6 +159,13 @@ export class OPCUAIf extends BaseIf {
164
159
  }
165
160
  }
166
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
+ */
167
169
  async call(opcuaNode, options = {}) {
168
170
  var res = this.Check(options)
169
171
  if (res < 0) {
@@ -171,7 +173,7 @@ export class OPCUAIf extends BaseIf {
171
173
  return false
172
174
  }
173
175
 
174
- var nodeId = super.formatString(opcuaNode, options)
176
+ var nodeId = super.call(opcuaNode, options)
175
177
 
176
178
  var type = this.types[nodeId]
177
179
  var value = options.value
@@ -211,6 +213,21 @@ export class OPCUAIf extends BaseIf {
211
213
  return 0
212
214
  }
213
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
+ */
214
231
  async Disconnect() {
215
232
  if (this.#client) {
216
233
  if (this.#session)
@@ -220,6 +237,10 @@ export class OPCUAIf extends BaseIf {
220
237
  this.LogInfo(`OPCUAIf: Disconnected\n`);
221
238
  }
222
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
+ */
223
244
  async Connect(url) {
224
245
  let self = this
225
246
  if (this.#client){
@@ -267,9 +288,9 @@ export class OPCUAIf extends BaseIf {
267
288
  this.#client.on("after_reconnection", (err) => {
268
289
  self.LogInfo(`OPCUAIf: After Reconnection event => ${err}\n`);
269
290
  });
270
- this.#client.on("security_token_renewed", () => {
291
+ /*this.#client.on("security_token_renewed", () => {
271
292
  self.LogDebug(`OPCUAIf: security_token_renewed\n`);
272
- })
293
+ })*/
273
294
  this.#client.on("lifetime_75", (token) => { })
274
295
 
275
296
  this.LogInfo(`OPCUAIf: connecting client to ${url}\n`);//, this.#session.toString());
@@ -299,16 +320,22 @@ export class OPCUAIf extends BaseIf {
299
320
  requestedPublishingInterval: this.#publishingInterval
300
321
  });
301
322
 
302
- this.LogInfo(`OPCUAIf: session created\n`);//, this.#session.toString());
303
- this.LogInfo(`OPCUAIf: client\n`);
304
- this.LogInfo(`OPCUAIf: subscription\n`);
323
+ this.LogInfo(`OPCUAIf: session created\n`);
324
+ // this.LogInfo(`OPCUAIf: client\n`);
325
+ // this.LogInfo(`OPCUAIf: subscription\n`);
305
326
  this.#connected = true
306
327
  this.#endpointurl = url
307
328
  }
308
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
+ */
309
337
  async Subscribe(nodeID) {
310
338
  // install monitored item
311
-
312
339
  const keys = Object.keys(this.monitoreditems)
313
340
  for (let i = 0; i < keys.length; i++) {
314
341
  const key = keys[i]
@@ -324,7 +351,7 @@ export class OPCUAIf extends BaseIf {
324
351
  attributeId: AttributeIds.Value
325
352
  };
326
353
  const monitoringParameters = {
327
- samplingInterval: 100,
354
+ samplingInterval: this.#samplingInterval,
328
355
  discardOldest: true,
329
356
  queueSize: 10
330
357
  };
@@ -390,12 +417,10 @@ export class OPCUAIf extends BaseIf {
390
417
  }
391
418
  } else {
392
419
  self.LogError("OPCUAIf: write NOT ok", nodeID, value, "\n");
393
- self.LogError(err)
394
420
  }
395
421
  });
396
422
  } catch (err) {
397
423
  self.LogError("OPCUAIf: write NOT ok", nodeID, value, "\n");
398
- self.LogError(err)
399
424
  }
400
425
  }
401
426
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loupedeck-commander",
3
- "version": "1.3.0",
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