loupedeck-commander 1.4.2 → 1.4.3
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/BaseLoupeDeckHandler.mjs +13 -4
- package/common/ButtonField.mjs +30 -19
- package/common/button.mjs +56 -21
- package/common/utils.mjs +28 -10
- package/interfaces/opcuaif.mjs +9 -5
- package/package.json +3 -2
|
@@ -14,6 +14,7 @@ export class BaseLoupeDeckHandler {
|
|
|
14
14
|
buttons = {}
|
|
15
15
|
knobs = {}
|
|
16
16
|
screenUpdate = {}
|
|
17
|
+
counter = 0
|
|
17
18
|
stopping = false
|
|
18
19
|
IntervalID = undefined
|
|
19
20
|
|
|
@@ -247,11 +248,11 @@ export class BaseLoupeDeckHandler {
|
|
|
247
248
|
//await this.updateScreens()
|
|
248
249
|
|
|
249
250
|
this.IntervalID = setInterval(() => {
|
|
250
|
-
// runs every
|
|
251
|
+
// runs every 50ms seconds
|
|
251
252
|
if (this.device.displays.center)
|
|
252
253
|
this.screenUpdate["center"] = true
|
|
253
254
|
self.updateScreens()
|
|
254
|
-
},
|
|
255
|
+
}, 50);
|
|
255
256
|
|
|
256
257
|
|
|
257
258
|
console.info('✅ Done initializing')
|
|
@@ -261,12 +262,20 @@ export class BaseLoupeDeckHandler {
|
|
|
261
262
|
* Fore an update of all screens
|
|
262
263
|
*/
|
|
263
264
|
async updateScreens () {
|
|
265
|
+
this.counter = this.counter + 1
|
|
264
266
|
const keys = Object.keys(this.screenUpdate)
|
|
265
267
|
for (let i = 0; i < keys.length; i++) {
|
|
266
268
|
const screen = keys[i]
|
|
267
269
|
//ok = await this.screens.center.updateAllButtons()
|
|
268
|
-
if (this.screens[screen])
|
|
269
|
-
|
|
270
|
+
if (this.screens[screen]){
|
|
271
|
+
if (this.counter % 5 == 0) { // Only check for blink every 5 times, to reduce the load on the device and avoid flickering
|
|
272
|
+
await this.screens[screen].draw(this.device)
|
|
273
|
+
}else{
|
|
274
|
+
await this.screens[screen].updateAllButtons(this.device)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
}
|
|
278
|
+
|
|
270
279
|
}
|
|
271
280
|
this.screenUpdate = {}
|
|
272
281
|
}
|
package/common/ButtonField.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Button} from './button.mjs'
|
|
1
|
+
import { Button } from './button.mjs'
|
|
2
2
|
|
|
3
3
|
export class ButtonField {
|
|
4
4
|
#buttons = {}
|
|
@@ -11,8 +11,8 @@ export class ButtonField {
|
|
|
11
11
|
#type
|
|
12
12
|
#name
|
|
13
13
|
#profile
|
|
14
|
-
|
|
15
|
-
constructor
|
|
14
|
+
|
|
15
|
+
constructor(name, rows, columns, width, height, data, profile) {
|
|
16
16
|
console.info(`ButtonField ${name.padEnd(10, ' ')} Buttons: ${rows} x ${columns} , Pixels ${width} x ${height}`)
|
|
17
17
|
this.#name = name
|
|
18
18
|
this.width = width
|
|
@@ -27,15 +27,25 @@ export class ButtonField {
|
|
|
27
27
|
const keys = Object.keys(data)
|
|
28
28
|
for (let i = 0; i < keys.length; i++) {
|
|
29
29
|
const key = keys[i]
|
|
30
|
-
const tb = new Button(`${this.#type}-${key}`, width / columns, height / rows, data[key],key,this.#profile)
|
|
30
|
+
const tb = new Button(`${this.#type}-${key}`, width / columns, height / rows, data[key], key, this.#profile)
|
|
31
31
|
this.#buttons[key] = tb
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
this.#keys = keys
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
async updateAllButtons(device) {
|
|
38
|
+
for (let i = 0; i < this.#keys.length; i++) {
|
|
39
|
+
const key = this.#keys[i]
|
|
40
|
+
const iValue = parseInt(key, 10)
|
|
41
|
+
const row = Math.floor(iValue / device.columns)
|
|
42
|
+
const column = iValue % device.columns
|
|
43
|
+
|
|
44
|
+
this.#buttons[key].draw(row, column)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async draw(device) {
|
|
39
49
|
if (!this.#screen) {
|
|
40
50
|
// physical buttons:
|
|
41
51
|
for (let i = 0; i < this.#keys.length; i++) {
|
|
@@ -58,7 +68,7 @@ export class ButtonField {
|
|
|
58
68
|
}
|
|
59
69
|
}
|
|
60
70
|
|
|
61
|
-
setState
|
|
71
|
+
setState(id, val) {
|
|
62
72
|
this.#buttons[id].setState(val)
|
|
63
73
|
}
|
|
64
74
|
|
|
@@ -66,7 +76,7 @@ export class ButtonField {
|
|
|
66
76
|
this.#buttons[id].setIntState(val)
|
|
67
77
|
}*/
|
|
68
78
|
|
|
69
|
-
async load
|
|
79
|
+
async load() {
|
|
70
80
|
for (let i = 0; i < this.#keys.length; i++) {
|
|
71
81
|
const key = this.#keys[i]
|
|
72
82
|
if (isNaN(key)) {
|
|
@@ -78,7 +88,7 @@ export class ButtonField {
|
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
async pressed
|
|
91
|
+
async pressed(id) {
|
|
82
92
|
this.checkAndCreateButton(id)
|
|
83
93
|
const result = await this.#buttons[id].pressed()
|
|
84
94
|
if (!result) {
|
|
@@ -92,7 +102,7 @@ export class ButtonField {
|
|
|
92
102
|
* @param {*} id
|
|
93
103
|
* @returns
|
|
94
104
|
*/
|
|
95
|
-
async released
|
|
105
|
+
async released(id) {
|
|
96
106
|
const result = await this.#buttons[id].released()
|
|
97
107
|
if (result) {
|
|
98
108
|
// disable all other buttons of the group, if this one had been activated:
|
|
@@ -103,7 +113,7 @@ export class ButtonField {
|
|
|
103
113
|
if (id === key) { continue }
|
|
104
114
|
let toggleGroup = this.#buttons[id].getGroup()
|
|
105
115
|
// Ignore empty groups
|
|
106
|
-
if (toggleGroup === undefined || toggleGroup == ""
|
|
116
|
+
if (toggleGroup === undefined || toggleGroup == "") { continue }
|
|
107
117
|
// Switch all other elements of the same group off:
|
|
108
118
|
if (this.#buttons[key].getGroup() === toggleGroup) {
|
|
109
119
|
this.#buttons[key].setState(0)
|
|
@@ -122,13 +132,13 @@ export class ButtonField {
|
|
|
122
132
|
* @param {*} nodeid
|
|
123
133
|
* @param {*} val
|
|
124
134
|
*/
|
|
125
|
-
async buttonStateChanged(changedButtonID,attribute,nodeid,val){
|
|
135
|
+
async buttonStateChanged(changedButtonID, attribute, nodeid, val) {
|
|
126
136
|
// todo: filter for buttonID
|
|
127
137
|
for (let i = 0; i < this.#keys.length; i++) {
|
|
128
138
|
let buttonID = this.#keys[i]
|
|
129
139
|
if (changedButtonID == buttonID)
|
|
130
|
-
await this.#buttons[buttonID].buttonStateChanged(i,attribute,nodeid,val)
|
|
131
|
-
}
|
|
140
|
+
await this.#buttons[buttonID].buttonStateChanged(i, attribute, nodeid, val)
|
|
141
|
+
}
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
/**
|
|
@@ -137,7 +147,7 @@ export class ButtonField {
|
|
|
137
147
|
* @param {*} delta
|
|
138
148
|
* @returns
|
|
139
149
|
*/
|
|
140
|
-
async rotated
|
|
150
|
+
async rotated(id, delta) {
|
|
141
151
|
this.checkAndCreateButton(id)
|
|
142
152
|
const result = await this.#buttons[id].rotated(delta)
|
|
143
153
|
if (!result) { console.info(`rotated ${this.#type} ${id} ${delta}`) }
|
|
@@ -151,14 +161,15 @@ export class ButtonField {
|
|
|
151
161
|
* @param {*} y
|
|
152
162
|
* @returns
|
|
153
163
|
*/
|
|
154
|
-
async touchmove
|
|
164
|
+
async touchmove(id, x, y) {
|
|
155
165
|
this.checkAndCreateButton(id)
|
|
156
166
|
const result = await this.#buttons[id].touchmove(x, y)
|
|
157
|
-
if (!result) {
|
|
158
|
-
|
|
167
|
+
//if (!result) {
|
|
168
|
+
// console.info(`touchmove ${id} ${x} ${y}`) }
|
|
169
|
+
//return result
|
|
159
170
|
}
|
|
160
171
|
|
|
161
|
-
checkAndCreateButton
|
|
172
|
+
checkAndCreateButton(id) {
|
|
162
173
|
if (!(id in this.#buttons)) {
|
|
163
174
|
const tb = new Button(id, 1, 1, id)
|
|
164
175
|
this.#buttons[id] = tb
|
package/common/button.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { loadImage } from 'canvas'
|
|
3
3
|
//import { loadImage } from "https://deno.land/x/canvas/mod.ts";
|
|
4
|
-
import { calcDelta,
|
|
4
|
+
import { calcDelta, darkerColor } from './utils.mjs'
|
|
5
5
|
import format from 'string-template'
|
|
6
6
|
|
|
7
7
|
import { shellinterface, httpinterface, opcuainterface, profileEmitter } from '../interfaces/interfaces.mjs'
|
|
@@ -21,9 +21,12 @@ export class Button {
|
|
|
21
21
|
#index = 0
|
|
22
22
|
#enforcedIndex = -1
|
|
23
23
|
#enforcedBlink = false
|
|
24
|
+
inversed = false
|
|
24
25
|
#event
|
|
25
26
|
#keys
|
|
26
27
|
#states
|
|
28
|
+
// PressActive
|
|
29
|
+
pressActive = false
|
|
27
30
|
// Timestamp when button was pressed
|
|
28
31
|
timeStampPressed
|
|
29
32
|
// Timestamp when button was released
|
|
@@ -37,9 +40,9 @@ export class Button {
|
|
|
37
40
|
this.#keys = []
|
|
38
41
|
|
|
39
42
|
this.id = id
|
|
40
|
-
this.#index = 0
|
|
41
|
-
this.#enforcedIndex = -1
|
|
42
|
-
this.#enforcedBlink = false
|
|
43
|
+
//this.#index = 0
|
|
44
|
+
//this.#enforcedIndex = -1
|
|
45
|
+
//this.#enforcedBlink = false
|
|
43
46
|
|
|
44
47
|
// console.info(` Button ${id.padEnd(10, ' ')} Size: ${width} x ${height}`)
|
|
45
48
|
|
|
@@ -193,19 +196,35 @@ export class Button {
|
|
|
193
196
|
async draw(row, column, ctx) {
|
|
194
197
|
let params = this.getParams(this.getCurrentElement())
|
|
195
198
|
let x = 0
|
|
196
|
-
let y = 0
|
|
199
|
+
let y = 0
|
|
197
200
|
|
|
198
201
|
// Adjust for non-square buttons:
|
|
199
202
|
if (params.width > params.height) {
|
|
200
|
-
x += (2+column) * (params.width - params.height) / 2
|
|
203
|
+
x += (2 + column) * (params.width - params.height) / 2
|
|
201
204
|
params.width = params.height
|
|
202
205
|
}
|
|
203
|
-
|
|
206
|
+
|
|
207
|
+
// If we have a filter for the event type, we check if the current event matches the filter. If not, we do not execute the command:
|
|
208
|
+
if (this.pressActive && params.filter == "released") {
|
|
209
|
+
this.timeHold = Date.now() - this.timeStampPressed
|
|
210
|
+
let overTime = this.timeHold - params.minPressed
|
|
211
|
+
if (overTime > 0 && overTime < 100) {
|
|
212
|
+
// Update the State according to the not correct pressed state
|
|
213
|
+
console.log('Minimum time of ', this.#params.minPressed, 'reached:', this.timeHold)
|
|
214
|
+
profileEmitter.emit("vibrate", 0x01) // Vibrate with a short pulse to indicate the button is pressed long enough
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
this.#counter = this.#counter + 1
|
|
218
|
+
// Only render the screen every 500ms (all 5 counter increments
|
|
219
|
+
if (!ctx) {
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
|
|
204
223
|
// Calculate the x/y position based on the row and column and the width/height properties of the button
|
|
205
224
|
x += column * params.width
|
|
206
|
-
y += row * params.height
|
|
225
|
+
y += row * params.height
|
|
226
|
+
|
|
207
227
|
|
|
208
|
-
|
|
209
228
|
// Modifications in draw:
|
|
210
229
|
// - Support color and textColor settings also in params section which
|
|
211
230
|
// can be overwriten by button state property. This way color and textColor
|
|
@@ -214,15 +233,24 @@ export class Button {
|
|
|
214
233
|
|
|
215
234
|
|
|
216
235
|
|
|
236
|
+
if (((params.blink === true) || this.#enforcedBlink)) {
|
|
237
|
+
this.inversed = !this.inversed
|
|
238
|
+
}
|
|
239
|
+
|
|
217
240
|
let color = params.color
|
|
218
241
|
let textColor = params.textColor
|
|
219
242
|
|
|
220
|
-
if (
|
|
221
|
-
// If blink is set, we switch textcolor and background color:
|
|
243
|
+
if (this.inversed) { // If blink is set, we switch textcolor and background color:
|
|
222
244
|
color = params.textColor
|
|
223
245
|
textColor = params.color
|
|
224
246
|
}
|
|
225
247
|
|
|
248
|
+
if (this.pressActive) {
|
|
249
|
+
// If the button is currently pressed, we apply a darken effect to the color:
|
|
250
|
+
color = darkerColor(color, 0.5)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
226
254
|
// Apply the color as fillStyle:
|
|
227
255
|
ctx.fillStyle = color
|
|
228
256
|
// Draw the background of the button::
|
|
@@ -269,8 +297,6 @@ export class Button {
|
|
|
269
297
|
}
|
|
270
298
|
ctx.fillText(dynamicText, tx, ty)
|
|
271
299
|
}
|
|
272
|
-
|
|
273
|
-
this.#counter = this.#counter + 1
|
|
274
300
|
}
|
|
275
301
|
|
|
276
302
|
async load(globalConfig) {
|
|
@@ -278,7 +304,11 @@ export class Button {
|
|
|
278
304
|
for (let i = 0; i < this.#keys.length; i++) {
|
|
279
305
|
const key = this.#keys[i]
|
|
280
306
|
const elem = this.#states[key]
|
|
281
|
-
|
|
307
|
+
let file = elem.image
|
|
308
|
+
if (file === undefined && this.#params.image) {
|
|
309
|
+
// If we do not have an image defined on state level, but we have an image defined on params level, we use the image from params:
|
|
310
|
+
file = this.#params.image
|
|
311
|
+
}
|
|
282
312
|
if (file !== undefined && file !== '') {
|
|
283
313
|
try {
|
|
284
314
|
this.#states[key].imgBuffer = await loadImage(file)
|
|
@@ -302,11 +332,12 @@ export class Button {
|
|
|
302
332
|
* @returns
|
|
303
333
|
*/
|
|
304
334
|
pressed() {
|
|
335
|
+
this.pressActive = true
|
|
305
336
|
this.timeStampPressed = Date.now()
|
|
306
337
|
this.updateState(this.#index, "pressed", true)
|
|
307
338
|
this.#index++
|
|
308
339
|
|
|
309
|
-
if (this.#enforcedIndex >= 0) {
|
|
340
|
+
if (this.#enforcedIndex >= 0 && this.#index != this.#enforcedIndex) {
|
|
310
341
|
console.log("Enforced Index", this.#enforcedIndex)
|
|
311
342
|
this.#index = this.#enforcedIndex
|
|
312
343
|
}
|
|
@@ -325,6 +356,8 @@ export class Button {
|
|
|
325
356
|
* @returns
|
|
326
357
|
*/
|
|
327
358
|
released() {
|
|
359
|
+
this.pressActive = false
|
|
360
|
+
this.inversed = false; // reset inversed state on release, so that blink starts from the defined color again on next press
|
|
328
361
|
let elem = this.getCurrentElement()
|
|
329
362
|
if (!elem) { return false }
|
|
330
363
|
this.timeStampReleased = Date.now()
|
|
@@ -405,22 +438,24 @@ export class Button {
|
|
|
405
438
|
// check if the state-name is same as the value we get from outside:
|
|
406
439
|
if (valStr == stateStr) {
|
|
407
440
|
//if (i !== this.#index) {
|
|
408
|
-
|
|
409
|
-
|
|
441
|
+
//console.info("enforce State index", this.id, nodeid, val, i)
|
|
442
|
+
this.#enforcedIndex = i;
|
|
410
443
|
//}
|
|
411
444
|
break;
|
|
412
445
|
}
|
|
413
446
|
}
|
|
414
447
|
//console.info("Changed index", this.id, nodeid, val, this.#enforcedIndex)
|
|
415
448
|
this.#index = this.#enforcedIndex;
|
|
449
|
+
this.inversed = false
|
|
450
|
+
this.pressActive = false
|
|
416
451
|
//console.info("enforce State index", this.id, nodeid, val, i)
|
|
417
452
|
//this.#enforcedIndex = this.#index;
|
|
418
453
|
}
|
|
419
|
-
|
|
454
|
+
|
|
420
455
|
|
|
421
456
|
if (attribute == "blink") {
|
|
422
|
-
|
|
423
|
-
|
|
457
|
+
console.info("Changed blink", buttonID, nodeid, val)
|
|
458
|
+
this.#enforcedBlink = val;
|
|
424
459
|
}
|
|
425
460
|
}
|
|
426
461
|
|
|
@@ -458,7 +493,7 @@ export class Button {
|
|
|
458
493
|
this.#params.y = (y % 100)
|
|
459
494
|
|
|
460
495
|
// Calculate delta for value no touchmove up/down
|
|
461
|
-
this.#params.
|
|
496
|
+
this.#params.touchvalue = calcDelta(this.#params.touchvalue, delta, this.#params.min, this.#params.max)
|
|
462
497
|
|
|
463
498
|
// console.log(`d: ${this.#params.moveDown} r: ${this.#params.moveRight} `)
|
|
464
499
|
return false
|
package/common/utils.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import YAML from 'yaml'
|
|
3
|
+
import JSON5 from 'json5'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Read a Config file in either JSON or YAML Fileformat
|
|
6
7
|
* @param {string} fileName - The name of the file to read
|
|
7
8
|
* @returns {object} - The parsed object from the file
|
|
8
9
|
*/
|
|
9
|
-
export function readConfigFile
|
|
10
|
+
export function readConfigFile(fileName) {
|
|
10
11
|
let obj = undefined
|
|
11
12
|
try {
|
|
12
13
|
//console.log("Reading File:", fileName)
|
|
@@ -14,14 +15,18 @@ export function readConfigFile (fileName) {
|
|
|
14
15
|
// If the file is a YAML file, parse it
|
|
15
16
|
if (fileName.endsWith('.yaml') || fileName.endsWith('.yml')) {
|
|
16
17
|
obj = YAML.parse(data)
|
|
17
|
-
} else if (fileName.endsWith('.json')) {
|
|
18
|
+
} else if (fileName.endsWith('.json') || fileName.endsWith('.json5')) {
|
|
18
19
|
// If the file is a JSON file, parse it
|
|
19
|
-
obj =
|
|
20
|
+
obj = JSON5.parse(data)
|
|
20
21
|
// automatically convert all json to yaml
|
|
21
|
-
writeYAMLFile(fileName.replace(".json",".yaml"), obj)
|
|
22
|
+
writeYAMLFile(fileName.replace(".json", ".yaml"), obj)
|
|
23
|
+
}
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (err.code === 'ENOENT') {
|
|
26
|
+
console.error(`Error reading file: File ${fileName} not found.`);
|
|
27
|
+
} else {
|
|
28
|
+
console.error(`Unexpected error: ${err.message}`);
|
|
22
29
|
}
|
|
23
|
-
} catch (error) {
|
|
24
|
-
console.info(`Error reading File: ${fileName}`, error)
|
|
25
30
|
}
|
|
26
31
|
return obj
|
|
27
32
|
}
|
|
@@ -30,12 +35,12 @@ export function readConfigFile (fileName) {
|
|
|
30
35
|
* Write a JSON File
|
|
31
36
|
*/
|
|
32
37
|
export function writeJSONFile (fileName, jsonObj) {
|
|
33
|
-
const data =
|
|
38
|
+
const data = JSON5.stringify(jsonObj, null, 4)
|
|
34
39
|
writeFileSync(fileName, data)
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
/**
|
|
38
|
-
* Write a
|
|
43
|
+
* Write a YAML File
|
|
39
44
|
*/
|
|
40
45
|
export function writeYAMLFile (fileName, jsonObj) {
|
|
41
46
|
const data = YAML.stringify(jsonObj)
|
|
@@ -68,12 +73,25 @@ export function invertColor(colorAsHex){
|
|
|
68
73
|
for (var i = 0; i < rgb.length; i++) {
|
|
69
74
|
rgb[i] = (i === 3 ? 1 : 255) - rgb[i];
|
|
70
75
|
}
|
|
71
|
-
let invertedColor = `#${rgb[0].toString(16)}${rgb[1].toString(16)}${rgb[2].toString(16)})`
|
|
72
|
-
//let invertedColor = `#${rgb[0].toString(16).padStart(2, '0')}${rgb[1].toString(16).padStart(2, '0')}${rgb[2].toString(16).padStart(2, '0')})`
|
|
76
|
+
let invertedColor = `#${ rgb[0].toString(16) }${ rgb[1].toString(16) }${ rgb[2].toString(16) })`
|
|
77
|
+
//let invertedColor = `#${ rgb[0].toString(16).padStart(2, '0') }${ rgb[1].toString(16).padStart(2, '0') }${ rgb[2].toString(16).padStart(2, '0') })`
|
|
73
78
|
//console.log("invert", colorAsHex,"=>",invertedColor)
|
|
74
79
|
return invertedColor
|
|
75
80
|
}
|
|
76
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Invert a color in hex format (#RRGGBB) to its inverted color
|
|
84
|
+
* @param {*} colorAsHex
|
|
85
|
+
* @returns
|
|
86
|
+
*/
|
|
87
|
+
export function darkerColor(colorAsHex, factor = 0.5){
|
|
88
|
+
let rgb = colorToRGB(colorAsHex)
|
|
89
|
+
for (var i = 0; i < rgb.length; i++) {
|
|
90
|
+
rgb[i] = factor* rgb[i];
|
|
91
|
+
}
|
|
92
|
+
return `#${ rgb[0].toString(16) }${ rgb[1].toString(16) }${ rgb[2].toString(16) })`
|
|
93
|
+
}
|
|
94
|
+
|
|
77
95
|
/**
|
|
78
96
|
* Convert a color in hex format (#RRGGBB) to RGB array
|
|
79
97
|
* @param {*} colorAsHex
|
package/interfaces/opcuaif.mjs
CHANGED
|
@@ -87,7 +87,7 @@ export class OPCUAIf extends BaseIf {
|
|
|
87
87
|
|
|
88
88
|
let monitoredItemId = await this.Subscribe(nodeID, options, attribute)
|
|
89
89
|
if (monitoredItemId) {
|
|
90
|
-
console.log(
|
|
90
|
+
console.log(`Register nodeid: ${nodeID}, monitoredItemId: ${monitoredItemId}, buttonID: ${buttonID}, attribute: ${attribute}`)
|
|
91
91
|
if (this.buttons[monitoredItemId] == undefined)
|
|
92
92
|
this.buttons[monitoredItemId] = []
|
|
93
93
|
this.buttons[monitoredItemId].push({
|
|
@@ -167,6 +167,8 @@ export class OPCUAIf extends BaseIf {
|
|
|
167
167
|
return value.toString();
|
|
168
168
|
return value
|
|
169
169
|
case DataType.Boolean:
|
|
170
|
+
if (typeof value == "boolean" )
|
|
171
|
+
return value
|
|
170
172
|
if (typeof value == "number" && value === 1)
|
|
171
173
|
return true
|
|
172
174
|
if (typeof value == "string") {
|
|
@@ -211,7 +213,7 @@ export class OPCUAIf extends BaseIf {
|
|
|
211
213
|
value = super.formatString(options.value, options)
|
|
212
214
|
|
|
213
215
|
var convertedValue = this.convert(value, type)
|
|
214
|
-
this.LogInfo(`OPCUAIf: write ${nodeId} => ${value}\n`)
|
|
216
|
+
//this.LogInfo(`OPCUAIf: write ${nodeId} => ${value}\n`)
|
|
215
217
|
await this.Write(nodeId, convertedValue, type)
|
|
216
218
|
|
|
217
219
|
var NewState = "waiting"
|
|
@@ -280,7 +282,7 @@ export class OPCUAIf extends BaseIf {
|
|
|
280
282
|
this.#client = OPCUAClient.create({
|
|
281
283
|
applicationName: "NodeOPCUA-Client",
|
|
282
284
|
endpointMustExist: true,
|
|
283
|
-
//
|
|
285
|
+
//keepSessionAlive: true,
|
|
284
286
|
requestedSessionTimeout: 60 * 1000,
|
|
285
287
|
securityMode: MessageSecurityMode.None,
|
|
286
288
|
securityPolicy: SecurityPolicy.None,
|
|
@@ -289,7 +291,7 @@ export class OPCUAIf extends BaseIf {
|
|
|
289
291
|
maxDelay: 5000,
|
|
290
292
|
initialDelay: 2500
|
|
291
293
|
},
|
|
292
|
-
|
|
294
|
+
keepPendingSessionsOnDisconnect: true,
|
|
293
295
|
defaultSecureTokenLifetime: 20000,
|
|
294
296
|
tokenRenewalInterval: 1000
|
|
295
297
|
});
|
|
@@ -424,11 +426,13 @@ export class OPCUAIf extends BaseIf {
|
|
|
424
426
|
self.types[nodeId] = dataValue.value.dataType
|
|
425
427
|
// publish the value to subscribers:
|
|
426
428
|
let buttons = self.buttons[this.monitoredItemId]
|
|
429
|
+
let list = []
|
|
427
430
|
for (var i = 0; i < buttons.length; i++) {
|
|
428
431
|
var buttonInfo = buttons[i]
|
|
429
|
-
|
|
432
|
+
list.push(`${buttonInfo.buttonID}:${buttonInfo.attribute}`)
|
|
430
433
|
self.emit('monitored item changed', buttonInfo.buttonID, buttonInfo.attribute, nodeId, dataValue.value.value)
|
|
431
434
|
}
|
|
435
|
+
self.LogInfo(`OPCUAIf: monitored item ${nodeId} changed value: ${dataValue.value.value} - push to buttons: ${list.join(', ')}\n`);
|
|
432
436
|
});
|
|
433
437
|
|
|
434
438
|
return monitoredItem.monitoredItemId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loupedeck-commander",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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": {
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"canvas": "^3.2.1",
|
|
14
|
+
"json5": "^2.2.3",
|
|
14
15
|
"loupedeck": "^7.0.3",
|
|
15
16
|
"mkdirp": "^3.0.1",
|
|
16
|
-
"node-opcua": "^2.
|
|
17
|
+
"node-opcua": "^2.163.1",
|
|
17
18
|
"string-template": "^1.0.0",
|
|
18
19
|
"yaml": "^2.8.2"
|
|
19
20
|
},
|