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