loupedeck-commander 1.2.11 → 1.3.0

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.
@@ -114,18 +114,18 @@ class Profile {
114
114
  */
115
115
  class TouchConfig {
116
116
  center = {
117
- "0" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
118
- "1" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
119
- "2" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
120
- "3" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
121
- "4" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
122
- "5" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
123
- "6" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
124
- "7" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
125
- "8" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
126
- "9" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
127
- "10" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
128
- "11" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } }, group : "" },
117
+ "0" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
118
+ "1" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
119
+ "2" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
120
+ "3" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
121
+ "4" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
122
+ "5" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
123
+ "6" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
124
+ "7" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
125
+ "8" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
126
+ "9" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
127
+ "10" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
128
+ "11" : { "states" : { "off" : { "color": "#000099", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" }, "on" : { "color": "#00ff00", "image": "icons/home.png","cmd": "echo \"{id} {state}\"" } } },
129
129
  } // CENTER Display Config - Available in CT & LIVE
130
130
  left = {
131
131
  "0" : { "states" : { "on" : { "color": "#000000", "cmd": "echo \"{id} {state}\"" } }, group : "" },
@@ -177,14 +177,14 @@ class TouchConfig {
177
177
  class ButtonConfig {
178
178
  buttons = {}
179
179
  defaultButtons = {
180
- "0" : {"states" : { "off" : { "filter": "pressed", "color": "#000000", "cmd": "echo \"{id} {state}\"" }, "on" : { "filter": "pressed","color": "#ff0000", "cmd": "echo \"{id} {state}\"" } }, group : "onoff" },
181
- "1" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", "cmd": "echo \"{id} {state}\"" }, "on" : { "filter": "pressed", "profile": 0, "color": "#00ff00", "cmd": "echo \"{id} {state}\"" } }, group : "profiles" },
182
- "2" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", "cmd": "echo \"{id} {state}\"" }, "on" : { "filter": "pressed", "profile": 1, "color": "#00ff00", "cmd": "echo \"{id} {state}\"" } }, group : "profiles" },
183
- "3" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", "cmd": "echo \"{id} {state}\"" }, "on" : { "filter": "pressed", "profile": 2, "color": "#00ff00", "cmd": "echo \"{id} {state}\"" } }, group : "profiles" },
184
- "4" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", "cmd": "echo \"{id} {state}\"" }, "on" : { "filter": "pressed", "profile": 3, "color": "#00ff00", "cmd": "echo \"{id} {state}\"" } }, group : "profiles" },
185
- "5" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", "cmd": "echo \"{id} {state}\"" }, "on" : { "filter": "pressed", "profile": 4, "color": "#00ff00", "cmd": "echo \"{id} {state}\"" } }, group : "profiles" },
186
- "6" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", "cmd": "echo \"{id} {state}\"" }, "on" : { "filter": "pressed", "profile": 5, "color": "#00ff00", "cmd": "echo \"{id} {state}\"" } }, group : "profiles" },
187
- "7" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", "cmd": "echo \"{id} {state}\"" }, "on" : { "filter": "pressed", "profile": 6, "color": "#00ff00", "cmd": "echo \"{id} {state}\"" } }, group : "profiles" },
180
+ "0" : {"states" : { "off" : { "filter": "pressed", "color": "#005500", "brightness" : 0 }, "half" : { "filter": "pressed", "brightness" : 0.5, "vibrate": 0x58, "color": "#00aa00" } , "on" : { "filter": "pressed", "brightness" : 1, "color": "#00ff00" } }, params: { group : "brightness"} },
181
+ "1" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", }, "on" : { "filter": "pressed", "profile": 0, "color": "#00ff00" } }, params: { group : "profiles"} },
182
+ "2" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", }, "on" : { "filter": "pressed", "profile": 1, "color": "#00ff00" } }, params: { group : "profiles"} },
183
+ "3" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", }, "on" : { "filter": "pressed", "profile": 2, "color": "#00ff00" } }, params: { group : "profiles"} },
184
+ "4" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", }, "on" : { "filter": "pressed", "profile": 3, "color": "#00ff00" } }, params: { group : "profiles"} },
185
+ "5" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", }, "on" : { "filter": "pressed", "profile": 4, "color": "#00ff00" } }, params: { group : "profiles"} },
186
+ "6" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", }, "on" : { "filter": "pressed", "profile": 5, "color": "#00ff00" } }, params: { group : "profiles"} },
187
+ "7" : {"states" : { "off" : { "filter": "pressed", "color": "#000055", }, "on" : { "filter": "pressed", "profile": 6, "color": "#00ff00" } }, params: { group : "profiles"} }
188
188
  }
189
189
 
190
190
  constructor (data,profileCount,index) {
@@ -1,17 +1,21 @@
1
1
  import {discover, HAPTIC,LoupedeckDevice } from 'loupedeck'
2
2
  import { ApplicationConfig } from './ApplicationConfig.mjs'
3
- import { ButtonField, StopInterfaces, InitializeInterfaces,opcuainterface,profileEmitter} from './touchbuttons.mjs'
3
+ import { ButtonField } from './ButtonField.mjs'
4
+
5
+ import { InitializeInterfaces, StopInterfaces, opcuainterface, profileEmitter } from '../interfaces/interfaces.mjs'
4
6
 
5
7
  export class BaseLoupeDeckHandler {
6
8
  device = undefined
7
9
  appConfig = undefined
8
10
  profileConfigs = []
9
11
  currentProfile = 0
12
+ brightness = 1
10
13
  screens = {}
11
14
  buttons = {}
12
15
  knobs = {}
13
16
  screenUpdate = {}
14
17
  stopping = false
18
+ IntervalID = undefined
15
19
 
16
20
  constructor (config) {
17
21
  console.log(`INIT with config ${config}`)
@@ -84,15 +88,15 @@ export class BaseLoupeDeckHandler {
84
88
  console.info(`Closing Device`)
85
89
  this.stopping = true
86
90
 
91
+ clearInterval(this.IntervalID)
92
+
87
93
  /* if (this.device){
88
94
  this.device.vibrate(HAPTIC.DESCEND_MED)
89
95
  await new Promise(resolve => setTimeout(resolve, 250))
90
96
  }
91
97
  */
92
- if (this.device){
93
- this.device.setBrightness(0)
98
+ this.changeBrightness(0)
94
99
  await new Promise(resolve => setTimeout(resolve, 250))
95
- }
96
100
  if (this.device){
97
101
  this.device.reconnectInterval = 0
98
102
  await this.device.close()
@@ -122,6 +126,25 @@ export class BaseLoupeDeckHandler {
122
126
  return this.appConfig.loadFromFile(fileName)
123
127
  }
124
128
 
129
+ vibrate (pattern) {
130
+ console.info(`Vibrate with pattern ${pattern}`)
131
+ if (this.device) {
132
+ this.device.vibrate(pattern)
133
+ } else {
134
+ console.warn("No Device connected, cannot vibrate")
135
+ }
136
+ }
137
+
138
+ changeBrightness (brightness) {
139
+ console.info(`Change Brightness to ${brightness}`)
140
+ this.brightness = brightness
141
+ if (this.device) {
142
+ this.device.setBrightness(brightness)
143
+ } else {
144
+ console.warn("No Device connected, cannot change brightness")
145
+ }
146
+ }
147
+
125
148
  /**
126
149
  * activate the profile with the givven ID
127
150
  */
@@ -168,7 +191,7 @@ export class BaseLoupeDeckHandler {
168
191
  this.screenUpdate["center"] = true
169
192
  this.screenUpdate["left"] = true
170
193
  this.screenUpdate["right"] = true
171
- await this.updateScreens()
194
+ //await this.updateScreens()
172
195
 
173
196
  await this.buttons.draw(this.device)
174
197
  // Initialize the Interfaces
@@ -193,26 +216,32 @@ export class BaseLoupeDeckHandler {
193
216
 
194
217
  await this.activateProfile(0)
195
218
 
196
- // move into activate profile function to call init with every profile change
197
- // var profile = this.getCurrentProfile()
198
- //await InitializeInterfaces(profile)
199
-
200
219
  const self = this
201
220
 
202
221
  // Register callback on monitored item change:
203
222
  opcuainterface.on("monitored item changed",(buttonID,nodeid,val) => { self.buttonStateChanged(buttonID,nodeid,val) })
204
223
  profileEmitter.on("profileChanged",(profileID) => { self.activateProfile(profileID) })
224
+ profileEmitter.on("brightnessChanged",(brightness) => { self.changeBrightness(brightness) })
225
+ profileEmitter.on("vibrate",(pattern) => { self.vibrate(pattern) })
226
+
205
227
 
206
228
 
207
- this.device.setBrightness(1)
208
- this.device.vibrate(HAPTIC.ASCEND_MED)
209
-
229
+ this.changeBrightness(1)
230
+ this.vibrate(HAPTIC.ASCEND_MED)
231
+
210
232
  // Fore update of all screens
211
233
  console.info('✅ Force Screen update')
212
234
  this.screenUpdate["center"] = true
213
235
  this.screenUpdate["left"] = true
214
236
  this.screenUpdate["right"] = true
215
- await this.updateScreens()
237
+ //await this.updateScreens()
238
+
239
+ this.IntervalID = setInterval(() => {
240
+ // runs every 500ms seconds
241
+ this.screenUpdate["center"] = true
242
+ self.updateScreens()
243
+ }, 500);
244
+
216
245
 
217
246
  console.info('✅ Done initializing')
218
247
  }
@@ -224,6 +253,7 @@ export class BaseLoupeDeckHandler {
224
253
  const keys = Object.keys(this.screenUpdate)
225
254
  for (let i = 0; i < keys.length; i++) {
226
255
  const screen = keys[i]
256
+ //ok = await this.screens.center.updateAllButtons()
227
257
  await this.screens[screen].draw(this.device)
228
258
  }
229
259
  this.screenUpdate = {}
@@ -286,7 +316,7 @@ export class BaseLoupeDeckHandler {
286
316
  if (this.screens[screen])
287
317
  ok = await this.screens[screen].pressed(id)
288
318
  }
289
- await this.updateScreens()
319
+ //await this.updateScreens()
290
320
  return ok
291
321
  }
292
322
 
@@ -327,7 +357,7 @@ export class BaseLoupeDeckHandler {
327
357
  if (this.screens[elem.screen])
328
358
  ok = await this.screens[elem.screen].released(id)
329
359
  }
330
- await this.updateScreens()
360
+ //await this.updateScreens()
331
361
  return ok
332
362
  }
333
363
 
@@ -343,7 +373,7 @@ export class BaseLoupeDeckHandler {
343
373
  ok = await this.screens.center.changed(buttonID,nodeid,val)
344
374
  ok = await this.knobs.changed(buttonID,nodeid,val)
345
375
 
346
- await this.updateScreens()
376
+ //await this.updateScreens()
347
377
  return ok
348
378
  }
349
379
 
@@ -0,0 +1,159 @@
1
+ import {Button} from './button.mjs'
2
+
3
+ export class ButtonField {
4
+ #buttons = {}
5
+ #screen
6
+ width = 0
7
+ height = 0
8
+ #keys = []
9
+ #type
10
+ #name
11
+ #profile
12
+
13
+ constructor (name, rows, columns, width, height, data, profile) {
14
+ console.info(`ButtonField ${name.padEnd(10, ' ')} Buttons: ${rows} x ${columns} , Pixels ${width} x ${height}`)
15
+ this.#name = name
16
+ this.width = width
17
+ this.height = height
18
+ // this.#rows = rows
19
+ // this.#columns = columns
20
+ this.#screen = this.width > 0 && this.height > 0
21
+ this.#type = 'button'
22
+ this.#profile = profile
23
+ if (this.#screen) { this.#type = 'touch' }
24
+
25
+ const keys = Object.keys(data)
26
+ for (let i = 0; i < keys.length; i++) {
27
+ const key = keys[i]
28
+ const tb = new Button(`${this.#type}-${key}`, width / columns, height / rows, data[key],key,this.#profile)
29
+ this.#buttons[key] = tb
30
+ }
31
+
32
+ this.#keys = keys
33
+ }
34
+
35
+
36
+ async draw (device) {
37
+ if (!this.#screen) {
38
+ // physical buttons:
39
+ for (let i = 0; i < this.#keys.length; i++) {
40
+ const key = this.#keys[i]
41
+ this.#buttons[key].drawPhysical(device, key)
42
+ }
43
+ } else {
44
+ // screen:
45
+ device.drawScreen(this.#name, ctx => {
46
+ ctx.globalCompositeOperation = 'source-atop'
47
+ for (let i = 0; i < this.#keys.length; i++) {
48
+ const key = this.#keys[i]
49
+ const iValue = parseInt(key, 10)
50
+ const row = Math.floor(iValue / device.columns)
51
+ const column = iValue % device.columns
52
+
53
+ this.#buttons[key].draw(row, column, ctx)
54
+ }
55
+ })
56
+ }
57
+ }
58
+
59
+ setState (id, val) {
60
+ this.#buttons[id].setState(val)
61
+ }
62
+
63
+ /*setIntState (id, val) {
64
+ this.#buttons[id].setIntState(val)
65
+ }*/
66
+
67
+ async load () {
68
+ for (let i = 0; i < this.#keys.length; i++) {
69
+ const key = this.#keys[i]
70
+ if (isNaN(key)) {
71
+ await this.#buttons[key].load(this.#profile)
72
+ } else {
73
+ const iVal = parseInt(key, 10)
74
+ await this.#buttons[iVal].load(this.#profile)
75
+ }
76
+ }
77
+ }
78
+
79
+ async pressed (id) {
80
+ this.checkAndCreateButton(id)
81
+ const result = await this.#buttons[id].pressed()
82
+ if (!result) {
83
+ console.info(`pressed ${this.#type} ${id}`)
84
+ }
85
+ return result
86
+ }
87
+
88
+ /**
89
+ * Button released event handler
90
+ * @param {*} id
91
+ * @returns
92
+ */
93
+ async released (id) {
94
+ const result = await this.#buttons[id].released()
95
+ if (result) {
96
+ // disable all other buttons of the group, if this one had been activated:
97
+ for (let i = 0; i < this.#keys.length; i++) {
98
+ let key = this.#keys[i]
99
+ if (!isNaN(key)) { key = parseInt(key, 10) }
100
+ if (id === key) { continue }
101
+ if (this.#buttons[key].group === this.#buttons[id].group) {
102
+ this.#buttons[key].setState(0)
103
+ }
104
+ }
105
+ }
106
+ if (!result) {
107
+ console.info(`released ${this.#type} ${id}`)
108
+ }
109
+ return result
110
+ }
111
+
112
+ /**
113
+ * Button changed event handler (after opcua value change)
114
+ * @param {*} buttonID
115
+ * @param {*} nodeid
116
+ * @param {*} val
117
+ */
118
+ async changed(buttonID,nodeid,val){
119
+ for (let i = 0; i < this.#keys.length; i++) {
120
+ let bID = this.#keys[i]
121
+ await this.#buttons[bID].changed(i,nodeid,val)
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Handle a rotation event for a knob
127
+ * @param {*} id : Knob ID (e.g. 'knobTL, 'knobTR', 'knobBL', 'knobBR')
128
+ * @param {*} delta
129
+ * @returns
130
+ */
131
+ async rotated (id, delta) {
132
+ this.checkAndCreateButton(id)
133
+ const result = await this.#buttons[id].rotated(delta)
134
+ if (!result) { console.info(`rotated ${this.#type} ${id} ${delta}`) }
135
+ return result
136
+ }
137
+
138
+ /**
139
+ * Handle a touchmove event for a touchbutton (available in left, center and right button fields)
140
+ * @param {*} id
141
+ * @param {*} x
142
+ * @param {*} y
143
+ * @returns
144
+ */
145
+ async touchmove (id, x, y) {
146
+ this.checkAndCreateButton(id)
147
+ const result = await this.#buttons[id].touchmove(x, y)
148
+ if (!result) { console.info(`touchmove ${id} ${x} ${y}`) }
149
+ return result
150
+ }
151
+
152
+ checkAndCreateButton (id) {
153
+ if (!(id in this.#buttons)) {
154
+ const tb = new Button(id, 1, 1, id)
155
+ this.#buttons[id] = tb
156
+ }
157
+ }
158
+ }
159
+