loupedeck-commander 1.3.0 → 1.3.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.
- package/README.md +3 -3
- package/common/ApplicationConfig.mjs +11 -4
- package/common/BaseLoupeDeckHandler.mjs +4 -4
- package/common/ButtonField.mjs +11 -4
- package/common/button.mjs +247 -187
- package/common/utils.mjs +28 -7
- package/config.yaml +6 -0
- package/interfaces/baseif.mjs +1 -1
- package/interfaces/opcuaif.mjs +139 -79
- package/package.json +3 -2
- package/profile-1.yaml +186 -0
- package/profile-2.yaml +213 -0
- package/test.mjs +15 -1
- package/config.json +0 -13
- package/profile-1.json +0 -268
- package/profile-2.json +0 -290
package/common/button.mjs
CHANGED
|
@@ -20,9 +20,10 @@ export class Button {
|
|
|
20
20
|
//#data
|
|
21
21
|
#index = 0
|
|
22
22
|
#enforcedIndex = -1
|
|
23
|
+
#enforcedBlink = false
|
|
23
24
|
#event
|
|
24
25
|
#keys
|
|
25
|
-
#states
|
|
26
|
+
#states
|
|
26
27
|
// Timestamp when button was pressed
|
|
27
28
|
timeStampPressed
|
|
28
29
|
// Timestamp when button was released
|
|
@@ -31,48 +32,49 @@ export class Button {
|
|
|
31
32
|
timeHold
|
|
32
33
|
#counter = 0
|
|
33
34
|
|
|
34
|
-
constructor
|
|
35
|
+
constructor(id, width, height, data = {}, key, profile = {}) {
|
|
35
36
|
this.#states = {}
|
|
36
37
|
this.#keys = []
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
this.id = id
|
|
39
40
|
this.#index = 0
|
|
40
41
|
this.#enforcedIndex = -1
|
|
42
|
+
this.#enforcedBlink = false
|
|
41
43
|
|
|
42
44
|
this.#profile = profile
|
|
43
45
|
this.#params = {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (profile.parameters){
|
|
46
|
+
"key": key,
|
|
47
|
+
"state": '',
|
|
48
|
+
"width": width,
|
|
49
|
+
"height": height,
|
|
50
|
+
"min": 0,
|
|
51
|
+
"max": 100,
|
|
52
|
+
"x": 0,
|
|
53
|
+
"y": 0,
|
|
54
|
+
"color": '#000000',
|
|
55
|
+
"text": '',
|
|
56
|
+
"textColor": '#ffffff',
|
|
57
|
+
"textAlign": 'center',
|
|
58
|
+
"textBaseline": 'top',
|
|
59
|
+
"font": '16px Arial',
|
|
60
|
+
"value": 50,
|
|
61
|
+
"minPressed": 25,
|
|
62
|
+
"moveLeft": false,
|
|
63
|
+
"moveRight": false,
|
|
64
|
+
"moveUp": false,
|
|
65
|
+
"moveDown": false,
|
|
66
|
+
"nodeid": '',
|
|
67
|
+
"type": ButtonType.TOGGLE,
|
|
68
|
+
"group": key,
|
|
69
|
+
"filter": "",
|
|
70
|
+
"blink": false,
|
|
71
|
+
"vibrate": false,
|
|
72
|
+
"profile": "",
|
|
73
|
+
"brightness": undefined,
|
|
74
|
+
"default": "0"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (profile.parameters) {
|
|
76
78
|
let params = Object.keys(profile.parameters)
|
|
77
79
|
for (let i = 0; i < params.length; i++) {
|
|
78
80
|
const k = params[i]
|
|
@@ -83,7 +85,7 @@ export class Button {
|
|
|
83
85
|
this.#params.key = key
|
|
84
86
|
|
|
85
87
|
// New approach: Use all data from data.params:
|
|
86
|
-
if (data.params){
|
|
88
|
+
if (data.params) {
|
|
87
89
|
this.#params = {
|
|
88
90
|
...this.#params,
|
|
89
91
|
...data.params
|
|
@@ -98,16 +100,16 @@ export class Button {
|
|
|
98
100
|
this.#states = data.states
|
|
99
101
|
this.#keys = Object.keys(this.#states)
|
|
100
102
|
|
|
101
|
-
var defaultIndex = this.#keys.indexOf(this.#params.default)
|
|
103
|
+
var defaultIndex = this.#keys.indexOf(this.#params.default)
|
|
102
104
|
if (defaultIndex >= 0) {
|
|
103
105
|
this.#index = defaultIndex
|
|
104
106
|
} else {
|
|
105
107
|
this.#params.default = this.#keys[0]
|
|
106
|
-
console.info(` button ${id} default set to ${this.#params.default}, available states`, this.#keys)
|
|
108
|
+
// console.info(` button ${id} default set to ${this.#params.default}, available states`, this.#keys)
|
|
107
109
|
}
|
|
108
110
|
this.#params.state = this.#keys[this.#index]
|
|
109
111
|
}
|
|
110
|
-
|
|
112
|
+
|
|
111
113
|
// Set the default value for the button:
|
|
112
114
|
|
|
113
115
|
// Legacy approach: Use data directly:
|
|
@@ -119,11 +121,11 @@ export class Button {
|
|
|
119
121
|
console.warn("Legacy approach: text is deprecated, use params.text instead")
|
|
120
122
|
this.#params.text = data.text
|
|
121
123
|
}
|
|
122
|
-
if (data.nodeid){
|
|
124
|
+
if (data.nodeid) {
|
|
123
125
|
this.#params.nodeid = format(data.nodeid, this.getParams({}))
|
|
124
|
-
console.warn("Legacy approach: nodeid is deprecated, use params.nodeid instead",this.#params.nodeid)
|
|
126
|
+
console.warn("Legacy approach: nodeid is deprecated, use params.nodeid instead", this.#params.nodeid)
|
|
125
127
|
}
|
|
126
|
-
if (data.type){
|
|
128
|
+
if (data.type) {
|
|
127
129
|
console.warn("Legacy approach: type is deprecated, use params.type instead")
|
|
128
130
|
this.#params.type = data.type
|
|
129
131
|
}
|
|
@@ -133,18 +135,32 @@ export class Button {
|
|
|
133
135
|
this.#index = this.#keys.indexOf(data.default)
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
if (data.group){
|
|
138
|
+
if (data.group) {
|
|
137
139
|
console.warn("Legacy approach: group is deprecated, use params.group instead")
|
|
138
140
|
this.#params.group = format(data.group, this.getParams({}))
|
|
139
141
|
}
|
|
140
142
|
// End Legacy approach
|
|
141
143
|
}
|
|
142
144
|
|
|
143
|
-
setState
|
|
145
|
+
setState(index = 0) {
|
|
144
146
|
this.#index = index
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
|
|
149
|
+
/**
|
|
150
|
+
*
|
|
151
|
+
* @returns Group attribute of the current button
|
|
152
|
+
*/
|
|
153
|
+
getGroup() {
|
|
154
|
+
return this.#params.group
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Draw a physical button (color in RGBA format) on a device
|
|
159
|
+
* @param {*} device
|
|
160
|
+
* @param {*} id - id of the physical button
|
|
161
|
+
* @returns
|
|
162
|
+
*/
|
|
163
|
+
async drawPhysical(device, id) {
|
|
148
164
|
const elem = this.getCurrentElement()
|
|
149
165
|
if (!elem || !elem.color) { return }
|
|
150
166
|
|
|
@@ -156,52 +172,95 @@ export class Button {
|
|
|
156
172
|
var idx = parseInt(id, 10);
|
|
157
173
|
|
|
158
174
|
const val = {
|
|
159
|
-
id:idx,
|
|
175
|
+
id: idx,
|
|
160
176
|
color: `rgba(${r}, ${g}, ${b})`
|
|
161
177
|
}
|
|
162
|
-
// console.log(' Set Button Color',id, val.id,elem.color, val.color)
|
|
178
|
+
// console.log(' Set Button Color',id, val.id,elem.color, val.color)
|
|
163
179
|
device.setButtonColor(val)
|
|
164
180
|
} catch (error) {
|
|
165
181
|
console.error(' Error', error)
|
|
166
182
|
}
|
|
167
183
|
}
|
|
168
184
|
|
|
169
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Draw the Touch-Screen Elementat the given row and column
|
|
187
|
+
* @param {*} row
|
|
188
|
+
* @param {*} column
|
|
189
|
+
* @param {*} ctx
|
|
190
|
+
*/
|
|
191
|
+
async draw(row, column, ctx) {
|
|
192
|
+
// Calculate the x/y position based on the row and column and the width/height properties of the button
|
|
170
193
|
const x = column * this.#params.width
|
|
171
194
|
const y = row * this.#params.height
|
|
172
195
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// If blink is set, we switch the color to inverted color
|
|
179
|
-
let oldColor = elem.color
|
|
180
|
-
elem.color = params.textColor
|
|
181
|
-
elem.textColor = oldColor
|
|
182
|
-
//console.log("Blinking Button",this.#index,elem.color)
|
|
183
|
-
}
|
|
196
|
+
// Modifications in draw:
|
|
197
|
+
// - Support color and textColor settings also in params section which
|
|
198
|
+
// can be overwriten by button state property. This way color and textColor
|
|
199
|
+
// can be defined overall for all states without repeating them in every state.
|
|
200
|
+
// - Same for blink property.
|
|
184
201
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
let params = this.getParams(this.getCurrentElement())
|
|
203
|
+
|
|
204
|
+
let color = params.color
|
|
205
|
+
let textColor = params.textColor
|
|
206
|
+
|
|
207
|
+
if ((params.blink || this.#enforcedBlink) && this.#counter % 2 == 0) {
|
|
208
|
+
// If blink is set, we switch textcolor and background color:
|
|
209
|
+
color = params.textColor
|
|
210
|
+
textColor = params.color
|
|
191
211
|
}
|
|
192
|
-
|
|
193
|
-
|
|
212
|
+
|
|
213
|
+
// Apply the color as fillStyle:
|
|
214
|
+
ctx.fillStyle = color
|
|
215
|
+
// Draw the background of the button::
|
|
216
|
+
ctx.fillRect(x, y, params.width, params.height)
|
|
217
|
+
|
|
218
|
+
// if we have an icon/image draw it on top of the button
|
|
219
|
+
if (params.imgBuffer) {
|
|
220
|
+
ctx.drawImage(params.imgBuffer, x, y, params.width, params.height)
|
|
221
|
+
}
|
|
222
|
+
// if we have a text defined, draw it on top:
|
|
223
|
+
if (params.text != undefined && params.text != '') {
|
|
224
|
+
ctx.fillStyle = textColor
|
|
194
225
|
ctx.font = params.font
|
|
195
226
|
ctx.textBaseline = params.textBaseline;
|
|
196
227
|
ctx.textAlign = params.textAlign;
|
|
197
|
-
let dynamicText = format(params.text,
|
|
198
|
-
|
|
228
|
+
let dynamicText = format(params.text, params)
|
|
229
|
+
let tx = x
|
|
230
|
+
let ty = y
|
|
231
|
+
// Handle Text Horizontal Alignment
|
|
232
|
+
switch (params.textAlign) {
|
|
233
|
+
case "center":
|
|
234
|
+
tx += this.#params.width / 2;
|
|
235
|
+
break;
|
|
236
|
+
case "left":
|
|
237
|
+
tx += 6;
|
|
238
|
+
break;
|
|
239
|
+
case "right":
|
|
240
|
+
tx += this.#params.width - 6;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
// Handle Text Vertical Alignment
|
|
244
|
+
switch (params.textBaseline) {
|
|
245
|
+
case "middle":
|
|
246
|
+
ty += this.#params.height / 2;
|
|
247
|
+
break;
|
|
248
|
+
case "top":
|
|
249
|
+
ty += 6;
|
|
250
|
+
ctx.textBaseline = "top";
|
|
251
|
+
break;
|
|
252
|
+
case "bottom":
|
|
253
|
+
ty += this.#params.height - 6;
|
|
254
|
+
ctx.textBaseline = "bottom";
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
ctx.fillText(dynamicText, x + 6, y + 6)
|
|
199
258
|
}
|
|
200
259
|
|
|
201
260
|
this.#counter = this.#counter + 1
|
|
202
261
|
}
|
|
203
262
|
|
|
204
|
-
async load
|
|
263
|
+
async load(globalConfig) {
|
|
205
264
|
this.#profile = globalConfig
|
|
206
265
|
for (let i = 0; i < this.#keys.length; i++) {
|
|
207
266
|
const key = this.#keys[i]
|
|
@@ -218,34 +277,41 @@ export class Button {
|
|
|
218
277
|
}
|
|
219
278
|
}
|
|
220
279
|
|
|
221
|
-
getCurrentElement
|
|
280
|
+
getCurrentElement() {
|
|
222
281
|
const state = this.#keys[this.#index]
|
|
223
282
|
return this.#states[state]
|
|
224
283
|
}
|
|
225
284
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
pressed () {
|
|
285
|
+
/**
|
|
286
|
+
* Triggered when either a physical button, touchscreen button or Knob Button was pressed
|
|
287
|
+
* Updates the timeStampPressed and timeHold, and checks if the button was hold long enough (minPressed attribute)
|
|
288
|
+
* If the button type was not TOGGLE, it will decrease the index to the previous state.
|
|
289
|
+
* @returns
|
|
290
|
+
*/
|
|
291
|
+
pressed() {
|
|
235
292
|
this.timeStampPressed = Date.now()
|
|
236
|
-
|
|
293
|
+
this.updateState(this.#index, "pressed", true)
|
|
237
294
|
this.#index++
|
|
238
295
|
|
|
239
|
-
if (this.#enforcedIndex>=0){
|
|
240
|
-
console.log("Enforced Index",this.#enforcedIndex
|
|
296
|
+
if (this.#enforcedIndex >= 0) {
|
|
297
|
+
console.log("Enforced Index", this.#enforcedIndex)
|
|
241
298
|
this.#index = this.#enforcedIndex
|
|
242
299
|
}
|
|
243
300
|
|
|
244
|
-
|
|
301
|
+
// Update the State according to the correctly pressed state
|
|
302
|
+
if (this.#index < 0) { this.#index = this.#keys.length - 1 }
|
|
303
|
+
this.#index %= this.#keys.length
|
|
304
|
+
|
|
245
305
|
return true
|
|
246
306
|
}
|
|
247
307
|
|
|
248
|
-
|
|
308
|
+
/**
|
|
309
|
+
* Triggered when either a physical button, touchscreen button or Knob Button was released
|
|
310
|
+
* Updates the timeStampReleased and timeHold, and checks if the button was hold long enough (minPressed attribute)
|
|
311
|
+
* If the button type was not TOGGLE, it will decrease the index to the previous state.
|
|
312
|
+
* @returns
|
|
313
|
+
*/
|
|
314
|
+
released() {
|
|
249
315
|
let elem = this.getCurrentElement()
|
|
250
316
|
if (!elem) { return false }
|
|
251
317
|
this.timeStampReleased = Date.now()
|
|
@@ -255,7 +321,6 @@ export class Button {
|
|
|
255
321
|
if (this.timeHold < this.#params.minPressed) {
|
|
256
322
|
// Update the State according to the not correct pressed state
|
|
257
323
|
console.log('Did not hold minimum time of ', this.#params.minPressed, 'only', this.timeHold)
|
|
258
|
-
//if (this.#index < 0) { this.#index = this.#keys.length - 1 }
|
|
259
324
|
bExecute = false
|
|
260
325
|
}
|
|
261
326
|
|
|
@@ -266,34 +331,41 @@ export class Button {
|
|
|
266
331
|
break
|
|
267
332
|
default:
|
|
268
333
|
this.#index--
|
|
269
|
-
//if (this.#index < 0) { this.#index = this.#keys.length - 1 }
|
|
270
334
|
break
|
|
271
335
|
}
|
|
272
336
|
|
|
273
|
-
if (this.#enforcedIndex>=0){
|
|
274
|
-
|
|
337
|
+
if (this.#enforcedIndex >= 0 && this.#enforcedIndex != this.#index) {
|
|
338
|
+
// If we have an enforced index, we set it to the enforced index:
|
|
339
|
+
console.log("Enforcing Index", this.#enforcedIndex, "instead of", this.#index)
|
|
275
340
|
this.#index = this.#enforcedIndex
|
|
276
341
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
this.updateState(this.#index,"released",bExecute)
|
|
280
|
-
|
|
281
|
-
return true // this.runCommand()
|
|
282
|
-
}
|
|
283
342
|
|
|
284
|
-
updateState(index,eventType,bExecute){
|
|
285
|
-
this.#index = index
|
|
286
|
-
this.#event = eventType
|
|
287
343
|
// Update the State according to the correctly pressed state
|
|
288
344
|
if (this.#index < 0) { this.#index = this.#keys.length - 1 }
|
|
289
345
|
this.#index %= this.#keys.length
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
this.updateState(this.#index, "released", bExecute)
|
|
349
|
+
|
|
350
|
+
return true
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
updateState(index, eventType, bExecute) {
|
|
355
|
+
this.#index = index
|
|
356
|
+
this.#event = eventType
|
|
290
357
|
if (bExecute)
|
|
291
358
|
this.runCommand()
|
|
292
|
-
|
|
293
|
-
return true // this.runCommand()
|
|
359
|
+
return true
|
|
294
360
|
}
|
|
295
361
|
|
|
296
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Triggered when one of the physical KNOBS is rotated
|
|
364
|
+
* Calculates the new value based on the delta, and store it in the params.value
|
|
365
|
+
* @param {*} delta
|
|
366
|
+
* @returns
|
|
367
|
+
*/
|
|
368
|
+
async rotated(delta) {
|
|
297
369
|
if (!this.getCurrentElement()) { return false }
|
|
298
370
|
|
|
299
371
|
this.#event = "rotated"
|
|
@@ -301,50 +373,51 @@ export class Button {
|
|
|
301
373
|
return this.runCommand()
|
|
302
374
|
}
|
|
303
375
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
let state = this.#states[key]
|
|
325
|
-
if (state.value === undefined)
|
|
326
|
-
continue
|
|
327
|
-
|
|
328
|
-
const params = {
|
|
329
|
-
id: buttonID,
|
|
330
|
-
key: buttonID,
|
|
331
|
-
state : key,
|
|
332
|
-
...state
|
|
333
|
-
}
|
|
334
|
-
if (state && state.value !== undefined){
|
|
335
|
-
let strVal = state.value.toString()
|
|
336
|
-
let val1 = format(strVal,params)
|
|
337
|
-
if (val1 === val?.toString()){
|
|
376
|
+
/**
|
|
377
|
+
* This function is called when the value of a button is changed from outside (e.g. coming from an opcua subscription)
|
|
378
|
+
* @param {*} buttonID: the ID of the button that caused the change
|
|
379
|
+
* @param {*} nodeid: the nodeid that changed
|
|
380
|
+
* @param {*} val : the current value of the given nodeid
|
|
381
|
+
* @returns
|
|
382
|
+
*/
|
|
383
|
+
async buttonStateChanged(buttonID, attribute, nodeid, val) {
|
|
384
|
+
|
|
385
|
+
//
|
|
386
|
+
if (attribute == "nodeid" && nodeid == this.#params.nodeid) {
|
|
387
|
+
this.#index = 0;
|
|
388
|
+
this.#enforcedIndex = -1;
|
|
389
|
+
for (let i = 0; i < this.#keys.length; i++) {
|
|
390
|
+
let stateStr = this.#keys[i].toString()
|
|
391
|
+
let valStr = val.toString()
|
|
392
|
+
// check if the state-name is same as the value we get from outside:
|
|
393
|
+
if (valStr == stateStr) {
|
|
394
|
+
if (i !== this.#index) {
|
|
395
|
+
console.info("Changed index", this.id, nodeid, val, i)
|
|
338
396
|
this.#index = i;
|
|
339
|
-
|
|
397
|
+
console.info("enforce State index", this.id, nodeid, val, i)
|
|
398
|
+
this.#enforcedIndex = this.#index;
|
|
340
399
|
}
|
|
400
|
+
break;
|
|
341
401
|
}
|
|
342
|
-
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (attribute == "blink") {
|
|
406
|
+
console.info("Changed blink", buttonID, nodeid, val)
|
|
407
|
+
this.#enforcedBlink = val;
|
|
343
408
|
}
|
|
344
409
|
}
|
|
345
410
|
|
|
346
|
-
|
|
347
|
-
|
|
411
|
+
/**
|
|
412
|
+
* Handle a touchmove event on a touchscreen button
|
|
413
|
+
* This function calculates the new value based on the x and y coordinates of the touchmove event.
|
|
414
|
+
* It updates the moveLeft, moveRight, moveUp and moveDown parameters based on the x and y coordinates.
|
|
415
|
+
* It also calculates the delta for the value based on the y coordinate and updates the value parameter.
|
|
416
|
+
* @param {*} x
|
|
417
|
+
* @param {*} y
|
|
418
|
+
* @returns
|
|
419
|
+
*/
|
|
420
|
+
async touchmove(x, y) {
|
|
348
421
|
|
|
349
422
|
let delta = 0
|
|
350
423
|
if (x > this.#params.x) {
|
|
@@ -365,8 +438,8 @@ export class Button {
|
|
|
365
438
|
delta = 1
|
|
366
439
|
}
|
|
367
440
|
|
|
368
|
-
this.#params.x = (x%100)
|
|
369
|
-
this.#params.y = (y%100)
|
|
441
|
+
this.#params.x = (x % 100)
|
|
442
|
+
this.#params.y = (y % 100)
|
|
370
443
|
|
|
371
444
|
// Calculate delta for value no touchmove up/down
|
|
372
445
|
this.#params.value = calcDelta(this.#params.value, delta, this.#params.min, this.#params.max)
|
|
@@ -375,7 +448,7 @@ export class Button {
|
|
|
375
448
|
return false
|
|
376
449
|
}
|
|
377
450
|
|
|
378
|
-
getParams(elem){
|
|
451
|
+
getParams(elem) {
|
|
379
452
|
// Call an action - include dynamic parameters
|
|
380
453
|
// and also all attributes of elem + global config
|
|
381
454
|
const params = {
|
|
@@ -385,71 +458,58 @@ export class Button {
|
|
|
385
458
|
id: this.id,
|
|
386
459
|
//key: this.key,
|
|
387
460
|
event: this.#event,
|
|
388
|
-
pressed
|
|
389
|
-
released
|
|
390
|
-
rotated
|
|
391
|
-
// state: this.#keys[this.#index]
|
|
461
|
+
pressed: this.#event == "pressed",
|
|
462
|
+
released: this.#event == "released",
|
|
463
|
+
rotated: this.#event == "rotated",
|
|
392
464
|
}
|
|
393
465
|
return params
|
|
394
466
|
}
|
|
395
467
|
|
|
396
|
-
|
|
397
|
-
|
|
468
|
+
/**
|
|
469
|
+
* Run a command based on the current element's parameters.
|
|
470
|
+
* This function checks if the current element has a command, http request or opcua request defined.
|
|
471
|
+
* If so, it will execute the command using the respective interface (shell, http or opcua).
|
|
472
|
+
* It will also emit profile and brightness changes if defined in the parameters.
|
|
473
|
+
* @returns
|
|
474
|
+
*/
|
|
475
|
+
async runCommand() {
|
|
476
|
+
//const elem = this.getCurrentElement()
|
|
477
|
+
let params = this.getParams(this.getCurrentElement())
|
|
478
|
+
|
|
398
479
|
// Only continue, if we have an element:
|
|
399
|
-
if (!
|
|
480
|
+
if (!params) {
|
|
400
481
|
return
|
|
401
482
|
}
|
|
402
483
|
// Filter for Event Type:
|
|
403
|
-
if (
|
|
484
|
+
if (params.filter && params.filter != this.#event) {
|
|
404
485
|
return
|
|
405
486
|
}
|
|
406
487
|
|
|
407
|
-
if (
|
|
408
|
-
profileEmitter.emit("profileChanged",
|
|
488
|
+
if (params.profile !== undefined && params.profile !== '') {
|
|
489
|
+
profileEmitter.emit("profileChanged", params.profile)
|
|
409
490
|
}
|
|
410
491
|
|
|
411
|
-
if (
|
|
412
|
-
|
|
492
|
+
if (params.brightness !== undefined && params.brightness !== '') {
|
|
493
|
+
// If brightness is set, we emit a brightnessChanged event with the given params
|
|
494
|
+
// This is used to change the brightness of the device
|
|
495
|
+
profileEmitter.emit("brightnessChanged", params.brightness)
|
|
413
496
|
}
|
|
414
497
|
|
|
415
|
-
if (
|
|
416
|
-
|
|
498
|
+
if (params.vibrate !== undefined && params.vibrate !== '' && params.vibrate !== false) {
|
|
499
|
+
// If vibrate is set, we emit a vibrate event with the given params
|
|
500
|
+
// This is used to trigger a vibration on the device
|
|
501
|
+
profileEmitter.emit("vibrate", params.vibrate)
|
|
417
502
|
}
|
|
418
503
|
|
|
419
|
-
// Only continue, if we have an element, that contains some kind of command:
|
|
420
|
-
if (!elem.cmd && !elem.http && !elem.opcua) {
|
|
421
|
-
return
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
let params = this.getParams(elem)
|
|
425
504
|
let res = ''
|
|
426
|
-
if ('cmd' in
|
|
427
|
-
|
|
428
|
-
res = await shellinterface.call(elem.cmd, params)
|
|
429
|
-
}else{
|
|
430
|
-
console.warn("shellinterface not started")
|
|
431
|
-
}
|
|
505
|
+
if ('cmd' in params && shellinterface) {
|
|
506
|
+
res = await shellinterface.call(params.cmd, params)
|
|
432
507
|
}
|
|
433
|
-
if ('http' in
|
|
434
|
-
|
|
435
|
-
res = await httpinterface.call(elem.http, params)
|
|
436
|
-
}else{
|
|
437
|
-
console.warn("httpinterface not started")
|
|
438
|
-
}
|
|
508
|
+
if ('http' in params && httpinterface) {
|
|
509
|
+
res = await httpinterface.call(params.http, params)
|
|
439
510
|
}
|
|
440
|
-
if ('opcua' in
|
|
441
|
-
|
|
442
|
-
res = await opcuainterface.call(elem.opcua, params)
|
|
443
|
-
|
|
444
|
-
/** TODO: CHECK THIS - don't think it's ever used any more */
|
|
445
|
-
/*if (this.#data.statenodeid){
|
|
446
|
-
let stateParams = params
|
|
447
|
-
params.value = params.state
|
|
448
|
-
res = await opcuainterface.call(this.#data.statenodeid, params)
|
|
449
|
-
}*/
|
|
450
|
-
}else{
|
|
451
|
-
console.warn("opcuainterface not started")
|
|
452
|
-
}
|
|
511
|
+
if ('opcua' in params && opcuainterface) {
|
|
512
|
+
res = await opcuainterface.call(params.opcua, params)
|
|
453
513
|
}
|
|
454
514
|
|
|
455
515
|
return res
|
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
|
|
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
|
|
7
|
-
let
|
|
9
|
+
export function readConfigFile (fileName) {
|
|
10
|
+
let obj = undefined
|
|
8
11
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
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
|