loupedeck-commander 1.2.7 → 1.2.8
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/ApplicationConfig.mjs +46 -10
- package/common/BaseLoupeDeckHandler.mjs +6 -2
- package/common/touchbuttons.mjs +41 -16
- package/common/utils.mjs +58 -3
- package/interfaces/opcuaif.mjs +17 -3
- package/package.json +1 -1
- package/numbers/README.md +0 -5
- package/numbers/five_9278222.png +0 -0
- package/numbers/four_9278183.png +0 -0
- package/numbers/one_9278045.png +0 -0
- package/numbers/six_9278279.png +0 -0
- package/numbers/three_9278150.png +0 -0
- package/numbers/two_9278103.png +0 -0
- package/profile-profile-1-sav.json +0 -373
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readJSONFile, writeJSONFile } from './utils.mjs'
|
|
1
|
+
import { readJSONFile, writeJSONFile,syncParams } from './utils.mjs'
|
|
2
2
|
|
|
3
3
|
export class ApplicationConfig {
|
|
4
4
|
application = 'undefined'
|
|
@@ -42,6 +42,7 @@ class Profile {
|
|
|
42
42
|
knobs = {}
|
|
43
43
|
buttons = {}
|
|
44
44
|
parameters = {}
|
|
45
|
+
default = {}
|
|
45
46
|
#file = ''
|
|
46
47
|
loaded = false
|
|
47
48
|
#error = true
|
|
@@ -57,6 +58,7 @@ class Profile {
|
|
|
57
58
|
this.buttons = new ButtonConfig().buttons
|
|
58
59
|
this.knobs = new KnobsConfig().knobs
|
|
59
60
|
this.parameters = new ParametersConfig().parameters
|
|
61
|
+
this.default = new DefaultConfig().default
|
|
60
62
|
|
|
61
63
|
this.loadFromFile(this.#file)
|
|
62
64
|
if (this.#error) { this.saveToFile(`profile-${this.name}-sav.json`) }
|
|
@@ -83,15 +85,16 @@ class Profile {
|
|
|
83
85
|
}
|
|
84
86
|
this.profile = config.profile
|
|
85
87
|
this.description = config.description
|
|
88
|
+
// Load Parameters.parameters = config.parameters
|
|
89
|
+
this.parameters = new ParametersConfig(config.parameters).parameters
|
|
90
|
+
this.default = new DefaultConfig(config.default).default
|
|
86
91
|
|
|
87
92
|
// Load the Configurations for Touch-Displays
|
|
88
|
-
this.touch = new TouchConfig(config.touch)
|
|
93
|
+
this.touch = new TouchConfig(config.touch,this.default)
|
|
89
94
|
// Load the Configurations for Button-Areas
|
|
90
95
|
|
|
91
96
|
this.buttons = new ButtonConfig(config.buttons,this.#profileCount,this.#index).buttons
|
|
92
97
|
this.knobs = new KnobsConfig(config.knobs).knobs
|
|
93
|
-
// Load Parameters.parameters = config.parameters
|
|
94
|
-
this.parameters = new ParametersConfig(config.parameters).parameters
|
|
95
98
|
|
|
96
99
|
this.#error = false
|
|
97
100
|
this.loaded = true
|
|
@@ -132,11 +135,11 @@ class TouchConfig {
|
|
|
132
135
|
} // RIGHT Display Config - Available in CT & LIVE
|
|
133
136
|
//knob = {} // KNOB Display Config - Available only in CT
|
|
134
137
|
|
|
135
|
-
constructor (data) {
|
|
136
|
-
this.loadFromJSON(data)
|
|
138
|
+
constructor (data,defaultState) {
|
|
139
|
+
this.loadFromJSON(data,defaultState)
|
|
137
140
|
}
|
|
138
141
|
|
|
139
|
-
loadFromJSON (data) {
|
|
142
|
+
loadFromJSON (data,defaultState) {
|
|
140
143
|
if (!data)
|
|
141
144
|
return
|
|
142
145
|
if (!data.center)
|
|
@@ -150,7 +153,21 @@ class TouchConfig {
|
|
|
150
153
|
this.center = data.center
|
|
151
154
|
this.left = data.left
|
|
152
155
|
this.right = data.right
|
|
153
|
-
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Sychronize the states of the buttons with the default state - add missing options from default state
|
|
159
|
+
*/
|
|
160
|
+
var buttons = Object.keys(this.center)
|
|
161
|
+
for (var buttonID=0;buttonID<buttons.length;buttonID++){
|
|
162
|
+
if (!this.center[buttonID])
|
|
163
|
+
continue
|
|
164
|
+
var states = Object.keys(this.center[buttonID].states)
|
|
165
|
+
for (var stateID=0;stateID<states.length;stateID++){
|
|
166
|
+
var stateKey = states[stateID]
|
|
167
|
+
var stateOld = this.center[buttonID].states[stateKey]
|
|
168
|
+
this.center[buttonID].states[stateKey] = syncParams(stateOld,defaultState)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
154
171
|
}
|
|
155
172
|
}
|
|
156
173
|
|
|
@@ -221,8 +238,10 @@ class ParametersConfig {
|
|
|
221
238
|
parameters = {
|
|
222
239
|
"hostname": "127.0.0.1",
|
|
223
240
|
"endpointurl": "opc.tcp://{hostname}:4840",
|
|
224
|
-
"nodeid" : "ns=0;s=nodeID"
|
|
225
|
-
|
|
241
|
+
"nodeid" : "ns=0;s=nodeID",
|
|
242
|
+
"min" : 0,
|
|
243
|
+
"max" : 100
|
|
244
|
+
}
|
|
226
245
|
|
|
227
246
|
constructor (data) {
|
|
228
247
|
this.loadFromJSON(data)
|
|
@@ -234,3 +253,20 @@ class ParametersConfig {
|
|
|
234
253
|
this.parameters = data
|
|
235
254
|
}
|
|
236
255
|
}
|
|
256
|
+
|
|
257
|
+
class DefaultConfig {
|
|
258
|
+
default = {
|
|
259
|
+
"filter": "released",
|
|
260
|
+
"color": "#111111"
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
constructor (data) {
|
|
264
|
+
this.loadFromJSON(data)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
loadFromJSON (data) {
|
|
268
|
+
if (!data)
|
|
269
|
+
return
|
|
270
|
+
this.default = data
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -166,6 +166,8 @@ export class BaseLoupeDeckHandler {
|
|
|
166
166
|
await this.updateScreens()
|
|
167
167
|
|
|
168
168
|
await this.buttons.draw(this.device)
|
|
169
|
+
// Initialize the Interfaces
|
|
170
|
+
await InitializeInterfaces(profile)
|
|
169
171
|
|
|
170
172
|
}
|
|
171
173
|
|
|
@@ -186,8 +188,9 @@ export class BaseLoupeDeckHandler {
|
|
|
186
188
|
|
|
187
189
|
await this.activateProfile(0)
|
|
188
190
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
// move into activate profile function to call init with every profile change
|
|
192
|
+
// var profile = this.getCurrentProfile()
|
|
193
|
+
//await InitializeInterfaces(profile)
|
|
191
194
|
|
|
192
195
|
const self = this
|
|
193
196
|
|
|
@@ -333,6 +336,7 @@ export class BaseLoupeDeckHandler {
|
|
|
333
336
|
this.screenUpdate["right"] = true
|
|
334
337
|
|
|
335
338
|
ok = await this.screens.center.changed(buttonID,nodeid,val)
|
|
339
|
+
ok = await this.knobs.changed(buttonID,nodeid,val)
|
|
336
340
|
|
|
337
341
|
await this.updateScreens()
|
|
338
342
|
return ok
|
package/common/touchbuttons.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import * as shellif from '../interfaces/shellif.mjs'
|
|
|
5
5
|
import * as httpif from '../interfaces/httpif.mjs'
|
|
6
6
|
import * as opcuaif from '../interfaces/opcuaif.mjs'
|
|
7
7
|
import format from 'string-template'
|
|
8
|
-
import { calcDelta } from './utils.mjs'
|
|
8
|
+
import { calcDelta, invertColor } from './utils.mjs'
|
|
9
9
|
import { EventEmitter } from 'node:events'
|
|
10
10
|
|
|
11
11
|
|
|
@@ -13,17 +13,18 @@ export var opcuainterface = undefined
|
|
|
13
13
|
var httpinterface = undefined
|
|
14
14
|
var shellinterface = undefined
|
|
15
15
|
export var profileEmitter = undefined
|
|
16
|
-
export async function InitializeInterfaces(appConfig
|
|
16
|
+
export async function InitializeInterfaces(appConfig){
|
|
17
17
|
if (opcuainterface === undefined ){
|
|
18
18
|
opcuainterface = new opcuaif.OPCUAIf()
|
|
19
|
-
opcuainterface.init(appConfig.parameters,appConfig,callbackFunction)
|
|
20
19
|
}
|
|
20
|
+
// the opcua interface needs the profile to register nodes with subscriptions:
|
|
21
|
+
opcuainterface.init(appConfig.parameters,appConfig)
|
|
21
22
|
if (httpinterface === undefined)
|
|
22
23
|
httpinterface = new httpif.HTTPif()
|
|
23
24
|
if (shellinterface === undefined)
|
|
24
25
|
shellinterface = new shellif.SHELLif()
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
if (profileEmitter === undefined)
|
|
27
|
+
profileEmitter = new EventEmitter()
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export async function StopInterfaces(){
|
|
@@ -81,7 +82,7 @@ export class ButtonField {
|
|
|
81
82
|
const keys = Object.keys(data)
|
|
82
83
|
for (let i = 0; i < keys.length; i++) {
|
|
83
84
|
const key = keys[i]
|
|
84
|
-
const tb = new Button(`${this.#type}-${key}`, width / columns, height / rows, data[key],key,this.#profile
|
|
85
|
+
const tb = new Button(`${this.#type}-${key}`, width / columns, height / rows, data[key],key,this.#profile)
|
|
85
86
|
this.#buttons[key] = tb
|
|
86
87
|
}
|
|
87
88
|
|
|
@@ -191,6 +192,8 @@ export class ButtonField {
|
|
|
191
192
|
|
|
192
193
|
export class Button {
|
|
193
194
|
#profile
|
|
195
|
+
#params
|
|
196
|
+
#data
|
|
194
197
|
width = 0
|
|
195
198
|
height = 0
|
|
196
199
|
|
|
@@ -229,7 +232,7 @@ export class Button {
|
|
|
229
232
|
minPressed = 25
|
|
230
233
|
key = -1
|
|
231
234
|
|
|
232
|
-
constructor (id, width, height, data,key,
|
|
235
|
+
constructor (id, width, height, data,key,profile) {
|
|
233
236
|
this.id = id
|
|
234
237
|
this.key = key
|
|
235
238
|
this.width = width
|
|
@@ -237,8 +240,10 @@ export class Button {
|
|
|
237
240
|
this.#index = 0
|
|
238
241
|
|
|
239
242
|
if (data && data.states) {
|
|
240
|
-
this
|
|
243
|
+
this.#data = data
|
|
241
244
|
|
|
245
|
+
this.group = data.group
|
|
246
|
+
|
|
242
247
|
this.#states = data.states
|
|
243
248
|
this.#keys = Object.keys(this.#states)
|
|
244
249
|
if (data.type) {
|
|
@@ -253,12 +258,23 @@ export class Button {
|
|
|
253
258
|
this.text = data.text
|
|
254
259
|
}
|
|
255
260
|
}
|
|
261
|
+
if (profile === undefined){
|
|
262
|
+
this.#profile = {}
|
|
263
|
+
this.#params = {}
|
|
264
|
+
}else{
|
|
265
|
+
this.#profile = profile
|
|
266
|
+
this.#params = profile.parameters
|
|
267
|
+
if (profile.parameters.min !== undefined)
|
|
268
|
+
this.#min = profile.parameters.min
|
|
269
|
+
if (profile.parameters.max !== undefined)
|
|
270
|
+
this.#max = profile.parameters.max
|
|
271
|
+
}
|
|
256
272
|
if (this.#states === undefined) {
|
|
257
273
|
this.#states = {}
|
|
258
274
|
this.#keys = []
|
|
259
275
|
}
|
|
260
276
|
if (data.nodeid){
|
|
261
|
-
this.#nodeid = format(data.nodeid, params)
|
|
277
|
+
this.#nodeid = format(data.nodeid, this.#params)
|
|
262
278
|
}
|
|
263
279
|
if (data.default) {
|
|
264
280
|
this.#index = this.#keys.indexOf(data.default)
|
|
@@ -308,8 +324,10 @@ export class Button {
|
|
|
308
324
|
}
|
|
309
325
|
}
|
|
310
326
|
if (this.text){
|
|
311
|
-
const lastElem = this.getLastElement()
|
|
312
|
-
|
|
327
|
+
//const lastElem = this.getLastElement()
|
|
328
|
+
// Only change the text color, if it differnce from the currently set color
|
|
329
|
+
|
|
330
|
+
ctx.fillStyle = invertColor(elem.color)
|
|
313
331
|
ctx.font = '20px Verdana'
|
|
314
332
|
ctx.textBaseline = 'top';
|
|
315
333
|
ctx.textAlign = 'left';
|
|
@@ -408,7 +426,7 @@ export class Button {
|
|
|
408
426
|
if (!this.getCurrentElement()) { return false }
|
|
409
427
|
|
|
410
428
|
this.#event = "rotated"
|
|
411
|
-
this.#value = calcDelta(this.#value, delta, this.#max)
|
|
429
|
+
this.#value = calcDelta(this.#value, delta, this.#min, this.#max)
|
|
412
430
|
return this.runCommand()
|
|
413
431
|
}
|
|
414
432
|
|
|
@@ -429,20 +447,21 @@ export class Button {
|
|
|
429
447
|
|
|
430
448
|
// check if the nodeid is the same and the value is one of the states
|
|
431
449
|
let state = this.#states[key]
|
|
432
|
-
if (
|
|
450
|
+
if (state.value === undefined)
|
|
433
451
|
continue
|
|
434
452
|
|
|
435
453
|
const params = {
|
|
436
454
|
id: buttonID,
|
|
437
455
|
key: buttonID,
|
|
456
|
+
state : key,
|
|
438
457
|
...state
|
|
439
458
|
}
|
|
440
459
|
let val1 = format(state.value,params)
|
|
441
|
-
if (val1 === val){
|
|
460
|
+
if (val1 === val.toString()){
|
|
442
461
|
this.#index = i;
|
|
443
462
|
break;
|
|
444
463
|
}
|
|
445
|
-
break;
|
|
464
|
+
//break;
|
|
446
465
|
}
|
|
447
466
|
}
|
|
448
467
|
|
|
@@ -507,7 +526,7 @@ export class Button {
|
|
|
507
526
|
y: (this.#y %100)
|
|
508
527
|
}
|
|
509
528
|
|
|
510
|
-
if (
|
|
529
|
+
if (params.value === undefined)
|
|
511
530
|
params.value = this.#value
|
|
512
531
|
|
|
513
532
|
|
|
@@ -529,6 +548,12 @@ export class Button {
|
|
|
529
548
|
if ('opcua' in elem) {
|
|
530
549
|
if (opcuainterface){
|
|
531
550
|
res = await opcuainterface.call(elem.opcua, params)
|
|
551
|
+
|
|
552
|
+
if (this.#data.statenodeid){
|
|
553
|
+
let stateParams = params
|
|
554
|
+
params.value = params.state
|
|
555
|
+
res = await opcuainterface.call(this.#data.statenodeid, params)
|
|
556
|
+
}
|
|
532
557
|
}else{
|
|
533
558
|
console.warn("opcuainterface not started")
|
|
534
559
|
}
|
package/common/utils.mjs
CHANGED
|
@@ -22,9 +22,64 @@ export function writeJSONFile (fileName, jsonObj) {
|
|
|
22
22
|
writeFileSync(fileName, data)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Calculate the delta of a value within a range between min and max with overflow
|
|
27
|
+
* @param {*} data
|
|
28
|
+
* @param {*} delta
|
|
29
|
+
* @param {*} min
|
|
30
|
+
* @param {*} max
|
|
31
|
+
* @returns
|
|
32
|
+
*/
|
|
33
|
+
export function calcDelta (data, delta, min = 0, max = 100) {
|
|
26
34
|
data = data + delta
|
|
27
|
-
if (data > max) { data =
|
|
28
|
-
if (data <
|
|
35
|
+
if (data > max) { data = min }
|
|
36
|
+
else if (data < min) { data = max }
|
|
29
37
|
return data
|
|
30
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Invert a color in hex format (#RRGGBB) to its inverted color
|
|
42
|
+
* @param {*} colorAsHex
|
|
43
|
+
* @returns
|
|
44
|
+
*/
|
|
45
|
+
export function invertColor(colorAsHex){
|
|
46
|
+
let rgb = colorToRGB(colorAsHex)
|
|
47
|
+
for (var i = 0; i < rgb.length; i++) {
|
|
48
|
+
rgb[i] = (i === 3 ? 1 : 255) - rgb[i];
|
|
49
|
+
}
|
|
50
|
+
let invertedColor = `#${rgb[0].toString(16)}${rgb[1].toString(16)}${rgb[2].toString(16)})`
|
|
51
|
+
//let invertedColor = `#${rgb[0].toString(16).padStart(2, '0')}${rgb[1].toString(16).padStart(2, '0')}${rgb[2].toString(16).padStart(2, '0')})`
|
|
52
|
+
//console.log("invert", colorAsHex,"=>",invertedColor)
|
|
53
|
+
return invertedColor
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Convert a color in hex format (#RRGGBB) to RGB array
|
|
58
|
+
* @param {*} colorAsHex
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
export function colorToRGB(colorAsHex){
|
|
62
|
+
const r = parseInt(colorAsHex.slice(1, 3), 16)
|
|
63
|
+
const g = parseInt(colorAsHex.slice(3, 5), 16)
|
|
64
|
+
const b = parseInt(colorAsHex.slice(5, 7), 16)
|
|
65
|
+
return [r,g,b]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Synchronize the parameters of a node with the default node
|
|
70
|
+
* @param {*} node
|
|
71
|
+
* @param {*} defaultnode
|
|
72
|
+
* @returns node
|
|
73
|
+
*/
|
|
74
|
+
export function syncParams(node, defaultnode){
|
|
75
|
+
if (node === undefined || defaultnode === undefined)
|
|
76
|
+
return node
|
|
77
|
+
let keys = Object.keys(defaultnode)
|
|
78
|
+
for (var i=0;i<keys.length;i++){
|
|
79
|
+
let key = keys[i]
|
|
80
|
+
if (!(key in node))
|
|
81
|
+
node[key] = defaultnode[key]
|
|
82
|
+
}
|
|
83
|
+
return node
|
|
84
|
+
}
|
|
85
|
+
|
package/interfaces/opcuaif.mjs
CHANGED
|
@@ -36,7 +36,6 @@ export class OPCUAIf extends BaseIf {
|
|
|
36
36
|
monitoreditems
|
|
37
37
|
types
|
|
38
38
|
buttons
|
|
39
|
-
#callback
|
|
40
39
|
constructor() {
|
|
41
40
|
super()
|
|
42
41
|
|
|
@@ -61,14 +60,13 @@ export class OPCUAIf extends BaseIf {
|
|
|
61
60
|
* @param {*} config
|
|
62
61
|
* @param {*} callbackFunction
|
|
63
62
|
*/
|
|
64
|
-
async init( options = {},config = {}
|
|
63
|
+
async init( options = {},config = {}){
|
|
65
64
|
var res = this.Check(options)
|
|
66
65
|
if (res<0){
|
|
67
66
|
this.LogError(`OPCUAIf: Missing essential options in dictionary => Quitting $res $options\n`)
|
|
68
67
|
}
|
|
69
68
|
try{
|
|
70
69
|
this.#endpointurl = this.formatString(options.endpointurl,options)
|
|
71
|
-
this.#callback = callbackFunction
|
|
72
70
|
this.monitoreditems = {}
|
|
73
71
|
this.types = {}
|
|
74
72
|
this.buttons = {}
|
|
@@ -84,11 +82,18 @@ export class OPCUAIf extends BaseIf {
|
|
|
84
82
|
for (let i = 0; i < keys.length; i++) {
|
|
85
83
|
const key = keys[i]
|
|
86
84
|
const elem = field[key]
|
|
85
|
+
// groupnode
|
|
87
86
|
if (elem.nodeid){
|
|
88
87
|
let format = this.formatString(elem.nodeid,options)
|
|
89
88
|
let monitoredItemId = await this.Subscribe(format)
|
|
90
89
|
this.buttons[monitoredItemId] = i
|
|
91
90
|
}
|
|
91
|
+
// statenode
|
|
92
|
+
if (elem.statenodeid){
|
|
93
|
+
let format = this.formatString(elem.statenodeid,options)
|
|
94
|
+
let monitoredItemId = await this.Subscribe(format)
|
|
95
|
+
this.buttons[monitoredItemId] = i
|
|
96
|
+
}
|
|
92
97
|
await this.monitorStates(elem,options)
|
|
93
98
|
}
|
|
94
99
|
}
|
|
@@ -146,6 +151,15 @@ export class OPCUAIf extends BaseIf {
|
|
|
146
151
|
if (typeof value == "number")
|
|
147
152
|
return value.toString();
|
|
148
153
|
return value
|
|
154
|
+
case DataType.Boolean:
|
|
155
|
+
if (typeof value == "number" && value === 1)
|
|
156
|
+
return true
|
|
157
|
+
if (typeof value == "string"){
|
|
158
|
+
if (["true","on"].includes(value)){
|
|
159
|
+
return true
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return false
|
|
149
163
|
default:
|
|
150
164
|
return value
|
|
151
165
|
}
|
package/package.json
CHANGED
package/numbers/README.md
DELETED
package/numbers/five_9278222.png
DELETED
|
Binary file
|
package/numbers/four_9278183.png
DELETED
|
Binary file
|
package/numbers/one_9278045.png
DELETED
|
Binary file
|
package/numbers/six_9278279.png
DELETED
|
Binary file
|
|
Binary file
|
package/numbers/two_9278103.png
DELETED
|
Binary file
|
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "profile-1",
|
|
3
|
-
"profile": "example",
|
|
4
|
-
"description": "",
|
|
5
|
-
"touch": {
|
|
6
|
-
"center": {
|
|
7
|
-
"0": {
|
|
8
|
-
"states": {
|
|
9
|
-
"off": {
|
|
10
|
-
"color": "#000099",
|
|
11
|
-
"image": "icons/home.png",
|
|
12
|
-
"cmd": "echo \"{id} {state}\""
|
|
13
|
-
},
|
|
14
|
-
"on": {
|
|
15
|
-
"color": "#00ff00",
|
|
16
|
-
"image": "icons/home.png",
|
|
17
|
-
"cmd": "echo \"{id} {state}\""
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"group": ""
|
|
21
|
-
},
|
|
22
|
-
"1": {
|
|
23
|
-
"states": {
|
|
24
|
-
"off": {
|
|
25
|
-
"color": "#000099",
|
|
26
|
-
"image": "icons/home.png",
|
|
27
|
-
"cmd": "echo \"{id} {state}\""
|
|
28
|
-
},
|
|
29
|
-
"on": {
|
|
30
|
-
"color": "#00ff00",
|
|
31
|
-
"image": "icons/home.png",
|
|
32
|
-
"cmd": "echo \"{id} {state}\""
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
"group": ""
|
|
36
|
-
},
|
|
37
|
-
"2": {
|
|
38
|
-
"states": {
|
|
39
|
-
"off": {
|
|
40
|
-
"color": "#000099",
|
|
41
|
-
"image": "icons/home.png",
|
|
42
|
-
"cmd": "echo \"{id} {state}\""
|
|
43
|
-
},
|
|
44
|
-
"on": {
|
|
45
|
-
"color": "#00ff00",
|
|
46
|
-
"image": "icons/home.png",
|
|
47
|
-
"cmd": "echo \"{id} {state}\""
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"group": ""
|
|
51
|
-
},
|
|
52
|
-
"3": {
|
|
53
|
-
"states": {
|
|
54
|
-
"off": {
|
|
55
|
-
"color": "#000099",
|
|
56
|
-
"image": "icons/home.png",
|
|
57
|
-
"cmd": "echo \"{id} {state}\""
|
|
58
|
-
},
|
|
59
|
-
"on": {
|
|
60
|
-
"color": "#00ff00",
|
|
61
|
-
"image": "icons/home.png",
|
|
62
|
-
"cmd": "echo \"{id} {state}\""
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
"group": ""
|
|
66
|
-
},
|
|
67
|
-
"4": {
|
|
68
|
-
"states": {
|
|
69
|
-
"off": {
|
|
70
|
-
"color": "#000099",
|
|
71
|
-
"image": "icons/home.png",
|
|
72
|
-
"cmd": "echo \"{id} {state}\""
|
|
73
|
-
},
|
|
74
|
-
"on": {
|
|
75
|
-
"color": "#00ff00",
|
|
76
|
-
"image": "icons/home.png",
|
|
77
|
-
"cmd": "echo \"{id} {state}\""
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
"group": ""
|
|
81
|
-
},
|
|
82
|
-
"5": {
|
|
83
|
-
"states": {
|
|
84
|
-
"off": {
|
|
85
|
-
"color": "#000099",
|
|
86
|
-
"image": "icons/home.png",
|
|
87
|
-
"cmd": "echo \"{id} {state}\""
|
|
88
|
-
},
|
|
89
|
-
"on": {
|
|
90
|
-
"color": "#00ff00",
|
|
91
|
-
"image": "icons/home.png",
|
|
92
|
-
"cmd": "echo \"{id} {state}\""
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
"group": ""
|
|
96
|
-
},
|
|
97
|
-
"6": {
|
|
98
|
-
"states": {
|
|
99
|
-
"off": {
|
|
100
|
-
"color": "#000099",
|
|
101
|
-
"image": "icons/home.png",
|
|
102
|
-
"cmd": "echo \"{id} {state}\""
|
|
103
|
-
},
|
|
104
|
-
"on": {
|
|
105
|
-
"color": "#00ff00",
|
|
106
|
-
"image": "icons/home.png",
|
|
107
|
-
"cmd": "echo \"{id} {state}\""
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
"group": ""
|
|
111
|
-
},
|
|
112
|
-
"7": {
|
|
113
|
-
"states": {
|
|
114
|
-
"off": {
|
|
115
|
-
"color": "#000099",
|
|
116
|
-
"image": "icons/home.png",
|
|
117
|
-
"cmd": "echo \"{id} {state}\""
|
|
118
|
-
},
|
|
119
|
-
"on": {
|
|
120
|
-
"color": "#00ff00",
|
|
121
|
-
"image": "icons/home.png",
|
|
122
|
-
"cmd": "echo \"{id} {state}\""
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
"group": ""
|
|
126
|
-
},
|
|
127
|
-
"8": {
|
|
128
|
-
"states": {
|
|
129
|
-
"off": {
|
|
130
|
-
"color": "#000099",
|
|
131
|
-
"image": "icons/home.png",
|
|
132
|
-
"cmd": "echo \"{id} {state}\""
|
|
133
|
-
},
|
|
134
|
-
"on": {
|
|
135
|
-
"color": "#00ff00",
|
|
136
|
-
"image": "icons/home.png",
|
|
137
|
-
"cmd": "echo \"{id} {state}\""
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
"group": ""
|
|
141
|
-
},
|
|
142
|
-
"9": {
|
|
143
|
-
"states": {
|
|
144
|
-
"off": {
|
|
145
|
-
"color": "#000099",
|
|
146
|
-
"image": "icons/home.png",
|
|
147
|
-
"cmd": "echo \"{id} {state}\""
|
|
148
|
-
},
|
|
149
|
-
"on": {
|
|
150
|
-
"color": "#00ff00",
|
|
151
|
-
"image": "icons/home.png",
|
|
152
|
-
"cmd": "echo \"{id} {state}\""
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
"group": ""
|
|
156
|
-
},
|
|
157
|
-
"10": {
|
|
158
|
-
"states": {
|
|
159
|
-
"off": {
|
|
160
|
-
"color": "#000099",
|
|
161
|
-
"image": "icons/home.png",
|
|
162
|
-
"cmd": "echo \"{id} {state}\""
|
|
163
|
-
},
|
|
164
|
-
"on": {
|
|
165
|
-
"color": "#00ff00",
|
|
166
|
-
"image": "icons/home.png",
|
|
167
|
-
"cmd": "echo \"{id} {state}\""
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
"group": ""
|
|
171
|
-
},
|
|
172
|
-
"11": {
|
|
173
|
-
"states": {
|
|
174
|
-
"off": {
|
|
175
|
-
"color": "#000099",
|
|
176
|
-
"image": "icons/home.png",
|
|
177
|
-
"cmd": "echo \"{id} {state}\""
|
|
178
|
-
},
|
|
179
|
-
"on": {
|
|
180
|
-
"color": "#00ff00",
|
|
181
|
-
"image": "icons/home.png",
|
|
182
|
-
"cmd": "echo \"{id} {state}\""
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
"group": ""
|
|
186
|
-
}
|
|
187
|
-
},
|
|
188
|
-
"left": {
|
|
189
|
-
"0": {
|
|
190
|
-
"states": {
|
|
191
|
-
"on": {
|
|
192
|
-
"color": "#000000",
|
|
193
|
-
"cmd": "echo \"{id} {state}\""
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
"group": ""
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
"right": {
|
|
200
|
-
"0": {
|
|
201
|
-
"states": {
|
|
202
|
-
"on": {
|
|
203
|
-
"color": "#000000",
|
|
204
|
-
"cmd": "echo \"{id} {state}\""
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
"group": ""
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
},
|
|
211
|
-
"knobs": {
|
|
212
|
-
"knobTL": {
|
|
213
|
-
"states": {
|
|
214
|
-
"on": {
|
|
215
|
-
"cmd": "echo \"{id} {state}\""
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
"group": ""
|
|
219
|
-
},
|
|
220
|
-
"knobCL": {
|
|
221
|
-
"states": {
|
|
222
|
-
"on": {
|
|
223
|
-
"cmd": "echo \"{id} {state}\""
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
"group": ""
|
|
227
|
-
},
|
|
228
|
-
"knobBL": {
|
|
229
|
-
"states": {
|
|
230
|
-
"on": {
|
|
231
|
-
"cmd": "echo \"{id} {state}\""
|
|
232
|
-
}
|
|
233
|
-
},
|
|
234
|
-
"group": ""
|
|
235
|
-
},
|
|
236
|
-
"knobTR": {
|
|
237
|
-
"states": {
|
|
238
|
-
"on": {
|
|
239
|
-
"cmd": "echo \"{id} {state}\""
|
|
240
|
-
}
|
|
241
|
-
},
|
|
242
|
-
"group": ""
|
|
243
|
-
},
|
|
244
|
-
"knobCR": {
|
|
245
|
-
"states": {
|
|
246
|
-
"on": {
|
|
247
|
-
"cmd": "echo \"{id} {state}\""
|
|
248
|
-
}
|
|
249
|
-
},
|
|
250
|
-
"group": ""
|
|
251
|
-
},
|
|
252
|
-
"knobBR": {
|
|
253
|
-
"states": {
|
|
254
|
-
"on": {
|
|
255
|
-
"cmd": "echo \"{id} {state}\""
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
"group": ""
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
"buttons": {
|
|
262
|
-
"0": {
|
|
263
|
-
"states": {
|
|
264
|
-
"off": {
|
|
265
|
-
"color": "#000000",
|
|
266
|
-
"cmd": "echo \"{id} {state}\""
|
|
267
|
-
},
|
|
268
|
-
"on": {
|
|
269
|
-
"color": "#550000",
|
|
270
|
-
"cmd": "echo \"{id} {state}\""
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
"group": ""
|
|
274
|
-
},
|
|
275
|
-
"1": {
|
|
276
|
-
"states": {
|
|
277
|
-
"off": {
|
|
278
|
-
"color": "#000000",
|
|
279
|
-
"cmd": "echo \"{id} {state}\""
|
|
280
|
-
},
|
|
281
|
-
"on": {
|
|
282
|
-
"color": "#ff0000",
|
|
283
|
-
"cmd": "echo \"{id} {state}\""
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
"group": ""
|
|
287
|
-
},
|
|
288
|
-
"2": {
|
|
289
|
-
"states": {
|
|
290
|
-
"off": {
|
|
291
|
-
"color": "#000000",
|
|
292
|
-
"cmd": "echo \"{id} {state}\""
|
|
293
|
-
},
|
|
294
|
-
"on": {
|
|
295
|
-
"color": "#005500",
|
|
296
|
-
"cmd": "echo \"{id} {state}\""
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
"group": ""
|
|
300
|
-
},
|
|
301
|
-
"3": {
|
|
302
|
-
"states": {
|
|
303
|
-
"off": {
|
|
304
|
-
"color": "#000000",
|
|
305
|
-
"cmd": "echo \"{id} {state}\""
|
|
306
|
-
},
|
|
307
|
-
"on": {
|
|
308
|
-
"color": "#00ff00",
|
|
309
|
-
"cmd": "echo \"{id} {state}\""
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
"group": ""
|
|
313
|
-
},
|
|
314
|
-
"4": {
|
|
315
|
-
"states": {
|
|
316
|
-
"off": {
|
|
317
|
-
"color": "#000000",
|
|
318
|
-
"cmd": "echo \"{id} {state}\""
|
|
319
|
-
},
|
|
320
|
-
"on": {
|
|
321
|
-
"color": "#000055",
|
|
322
|
-
"cmd": "echo \"{id} {state}\""
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
"group": ""
|
|
326
|
-
},
|
|
327
|
-
"5": {
|
|
328
|
-
"states": {
|
|
329
|
-
"off": {
|
|
330
|
-
"color": "#000000",
|
|
331
|
-
"cmd": "echo \"{id} {state}\""
|
|
332
|
-
},
|
|
333
|
-
"on": {
|
|
334
|
-
"color": "#0000ff",
|
|
335
|
-
"cmd": "echo \"{id} {state}\""
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
"group": ""
|
|
339
|
-
},
|
|
340
|
-
"6": {
|
|
341
|
-
"states": {
|
|
342
|
-
"off": {
|
|
343
|
-
"color": "#000000",
|
|
344
|
-
"cmd": "echo \"{id} {state}\""
|
|
345
|
-
},
|
|
346
|
-
"on": {
|
|
347
|
-
"color": "#555500",
|
|
348
|
-
"cmd": "echo \"{id} {state}\""
|
|
349
|
-
}
|
|
350
|
-
},
|
|
351
|
-
"group": ""
|
|
352
|
-
},
|
|
353
|
-
"7": {
|
|
354
|
-
"states": {
|
|
355
|
-
"off": {
|
|
356
|
-
"color": "#000000",
|
|
357
|
-
"cmd": "echo \"{id} {state}\""
|
|
358
|
-
},
|
|
359
|
-
"on": {
|
|
360
|
-
"color": "#ffff00",
|
|
361
|
-
"cmd": "echo \"{id} {state}\""
|
|
362
|
-
}
|
|
363
|
-
},
|
|
364
|
-
"group": ""
|
|
365
|
-
}
|
|
366
|
-
},
|
|
367
|
-
"parameters": {
|
|
368
|
-
"hostname": "127.0.0.1",
|
|
369
|
-
"endpointurl": "opc.tcp://{hostname}:4840",
|
|
370
|
-
"nodeid": "ns=0;s=nodeID"
|
|
371
|
-
},
|
|
372
|
-
"loaded": false
|
|
373
|
-
}
|