loupedeck-commander 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/common/ApplicationConfig.mjs +11 -4
- package/common/BaseLoupeDeckHandler.mjs +4 -4
- package/common/ButtonField.mjs +11 -4
- package/common/button.mjs +247 -187
- package/common/utils.mjs +28 -7
- package/config.yaml +6 -0
- package/interfaces/baseif.mjs +1 -1
- package/interfaces/opcuaif.mjs +139 -79
- package/package.json +3 -2
- package/profile-1.yaml +186 -0
- package/profile-2.yaml +213 -0
- package/test.mjs +15 -1
- package/config.json +0 -13
- package/profile-1.json +0 -268
- package/profile-2.json +0 -290
package/config.yaml
ADDED
package/interfaces/baseif.mjs
CHANGED
package/interfaces/opcuaif.mjs
CHANGED
|
@@ -2,27 +2,14 @@ import {
|
|
|
2
2
|
OPCUAClient,
|
|
3
3
|
MessageSecurityMode,
|
|
4
4
|
SecurityPolicy,
|
|
5
|
-
BrowseDirection,
|
|
6
5
|
AttributeIds,
|
|
7
|
-
NodeClassMask,
|
|
8
|
-
makeBrowsePath,
|
|
9
6
|
resolveNodeId,
|
|
10
7
|
TimestampsToReturn,
|
|
11
|
-
coerceInt32,
|
|
12
|
-
coerceByteString,
|
|
13
8
|
DataType
|
|
14
9
|
} from "node-opcua";
|
|
15
10
|
|
|
16
11
|
import { BaseIf } from './baseif.mjs'
|
|
17
12
|
|
|
18
|
-
const subscriptionParameters = {
|
|
19
|
-
maxNotificationsPerPublish: 1000,
|
|
20
|
-
publishingEnabled: true,
|
|
21
|
-
requestedLifetimeCount: 100,
|
|
22
|
-
requestedMaxKeepAliveCount: 10,
|
|
23
|
-
requestedPublishingInterval: 1000
|
|
24
|
-
};
|
|
25
|
-
|
|
26
13
|
/**
|
|
27
14
|
* Our Special-Handler just used the Default - and adds Vibration after triggers through Button-Releases
|
|
28
15
|
*/
|
|
@@ -34,20 +21,16 @@ export class OPCUAIf extends BaseIf {
|
|
|
34
21
|
#connected
|
|
35
22
|
#endpointurl
|
|
36
23
|
#publishingInterval
|
|
24
|
+
#samplingInterval
|
|
25
|
+
// Dictionary to store monitored items : key = monitoredItemId, value = nodeID
|
|
37
26
|
monitoreditems
|
|
38
27
|
types
|
|
28
|
+
// Dictionary to store buttons : key = monitoredItemId, value = buttonID
|
|
39
29
|
buttons
|
|
40
30
|
constructor() {
|
|
41
31
|
super()
|
|
42
32
|
}
|
|
43
33
|
|
|
44
|
-
async stop() {
|
|
45
|
-
if (!this.#client)
|
|
46
|
-
return
|
|
47
|
-
await this.Disconnect()
|
|
48
|
-
this.LogInfo(`OPCUAIf Stopped\n`)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
34
|
/**
|
|
52
35
|
* Initialize the OPCUA client and subscribe to monitored items.
|
|
53
36
|
* @param {*} options
|
|
@@ -63,10 +46,16 @@ export class OPCUAIf extends BaseIf {
|
|
|
63
46
|
this.#endpointurl = this.formatString(options.endpointurl, options)
|
|
64
47
|
if (options.publishingInterval)
|
|
65
48
|
this.#publishingInterval = options.publishingInterval
|
|
66
|
-
else{
|
|
49
|
+
else {
|
|
67
50
|
this.#publishingInterval = 250;
|
|
68
51
|
this.LogInfo(`OPCUAIf init using default publishingInterval: ${this.#publishingInterval}ms\n`);
|
|
69
52
|
}
|
|
53
|
+
if (options.samplingInterval)
|
|
54
|
+
this.#samplingInterval = options.samplingInterval
|
|
55
|
+
else {
|
|
56
|
+
this.#samplingInterval = 100;
|
|
57
|
+
this.LogInfo(`OPCUAIf init using default samplingInterval: ${this.#samplingInterval}ms\n`);
|
|
58
|
+
}
|
|
70
59
|
this.monitoreditems = {}
|
|
71
60
|
this.types = {}
|
|
72
61
|
this.buttons = {}
|
|
@@ -75,47 +64,65 @@ export class OPCUAIf extends BaseIf {
|
|
|
75
64
|
await this.Connect(this.#endpointurl);
|
|
76
65
|
|
|
77
66
|
let fields = [config.touch.center, config.knobs, config.buttons]
|
|
78
|
-
|
|
79
|
-
for (let f = 0; f < fieldKeys.length; f++) {
|
|
67
|
+
for (let f = 0; f < fields.length; f++) {
|
|
80
68
|
let field = fields[f]
|
|
81
|
-
const
|
|
69
|
+
const buttonNames = Object.keys(field)
|
|
82
70
|
|
|
83
71
|
// Iterate over buttons:
|
|
84
|
-
for (let i = 0; i <
|
|
85
|
-
const
|
|
86
|
-
const elem = field[
|
|
87
|
-
options["key"] =
|
|
72
|
+
for (let i = 0; i < buttonNames.length; i++) {
|
|
73
|
+
const buttonID = buttonNames[i]
|
|
74
|
+
const elem = field[buttonID]
|
|
75
|
+
options["key"] = buttonID // we have to know the key of the button
|
|
88
76
|
// groupnode
|
|
89
|
-
if (elem.params
|
|
90
|
-
let
|
|
91
|
-
let
|
|
92
|
-
|
|
93
|
-
|
|
77
|
+
if (elem.params) {
|
|
78
|
+
let attributes = ["nodeid", "blink"]
|
|
79
|
+
for (let j = 0; j < attributes.length; j++) {
|
|
80
|
+
const attribute = attributes[j]
|
|
81
|
+
if (attribute in elem.params) {
|
|
82
|
+
if (!this.IsNodeID(elem.params[attribute])) {
|
|
83
|
+
continue
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let monitoredItemId = await this.Subscribe(elem.params[attribute], options,attribute)
|
|
87
|
+
if (monitoredItemId) {
|
|
88
|
+
this.buttons[monitoredItemId] = {
|
|
89
|
+
buttonID: i,
|
|
90
|
+
buttonName: buttonID,
|
|
91
|
+
attribute: attribute
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (elem.states) {
|
|
98
|
+
let attributes = ["opcua", "blink"]
|
|
99
|
+
let states = Object.values(elem.states)
|
|
100
|
+
// Iterate over states:
|
|
101
|
+
for (let j = 0; j < states.length; j++) {
|
|
102
|
+
const state = states[j]
|
|
103
|
+
|
|
104
|
+
for (let k = 0; k < attributes.length; k++) {
|
|
105
|
+
const attribute = attributes[k]
|
|
106
|
+
if (attribute in state) {
|
|
107
|
+
if (!this.IsNodeID(state[attribute])) {
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
let monitoredItemId = await this.Subscribe(state[attribute], options,attribute)
|
|
111
|
+
if (monitoredItemId) {
|
|
112
|
+
this.buttons[monitoredItemId] = {
|
|
113
|
+
buttonID: i,
|
|
114
|
+
buttonName: buttonID,
|
|
115
|
+
attribute: attribute
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
94
121
|
}
|
|
95
|
-
await this.monitorStates(elem, options)
|
|
96
122
|
}
|
|
97
123
|
}
|
|
98
124
|
} catch (error) {
|
|
99
|
-
this.LogError(`OPCUAIf: Error\n`,error)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
105
|
-
* @param {*} elem : Elem Object
|
|
106
|
-
* @param {*} options
|
|
107
|
-
*/
|
|
108
|
-
async monitorStates(elem, options) {
|
|
109
|
-
const stateKeys = Object.keys(elem.states)
|
|
110
|
-
for (let i = 0; i < stateKeys.length; i++) {
|
|
111
|
-
const key2 = stateKeys[i]
|
|
112
|
-
const state = elem.states[key2]
|
|
113
|
-
if (state.opcua) {
|
|
114
|
-
let formattedNodeId = this.formatString(state.opcua, options)
|
|
115
|
-
let monitoredItemId = await this.Subscribe(formattedNodeId)
|
|
116
|
-
console.log("Subscribe to",monitoredItemId,formattedNodeId)
|
|
117
|
-
this.buttons[monitoredItemId] = i
|
|
118
|
-
}
|
|
125
|
+
this.LogError(`OPCUAIf: Error\n`, error)
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
128
|
|
|
@@ -164,6 +171,13 @@ export class OPCUAIf extends BaseIf {
|
|
|
164
171
|
}
|
|
165
172
|
}
|
|
166
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Call the OPCUA node with the given options.
|
|
176
|
+
* This method will write the value to the node and return the new state.
|
|
177
|
+
* @param {*} opcuaNode
|
|
178
|
+
* @param {*} options
|
|
179
|
+
* @returns
|
|
180
|
+
*/
|
|
167
181
|
async call(opcuaNode, options = {}) {
|
|
168
182
|
var res = this.Check(options)
|
|
169
183
|
if (res < 0) {
|
|
@@ -171,7 +185,7 @@ export class OPCUAIf extends BaseIf {
|
|
|
171
185
|
return false
|
|
172
186
|
}
|
|
173
187
|
|
|
174
|
-
var nodeId = super.
|
|
188
|
+
var nodeId = super.call(opcuaNode, options)
|
|
175
189
|
|
|
176
190
|
var type = this.types[nodeId]
|
|
177
191
|
var value = options.value
|
|
@@ -211,21 +225,40 @@ export class OPCUAIf extends BaseIf {
|
|
|
211
225
|
return 0
|
|
212
226
|
}
|
|
213
227
|
|
|
228
|
+
/**
|
|
229
|
+
* This method is called when the interface is stopped.
|
|
230
|
+
* It will disconnect the client and stop the subscription.
|
|
231
|
+
* @returns
|
|
232
|
+
*/
|
|
233
|
+
async stop() {
|
|
234
|
+
if (!this.#client)
|
|
235
|
+
return
|
|
236
|
+
await this.Disconnect()
|
|
237
|
+
this.LogInfo(`OPCUAIf Stopped\n`)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Disconnect the OPCUA client and close the session.
|
|
242
|
+
*/
|
|
214
243
|
async Disconnect() {
|
|
215
244
|
if (this.#client) {
|
|
216
245
|
if (this.#session)
|
|
217
|
-
await this.#client.closeSession(this.#session,true)
|
|
246
|
+
await this.#client.closeSession(this.#session, true)
|
|
218
247
|
this.#session = undefined
|
|
219
248
|
this.#client = undefined
|
|
220
249
|
this.LogInfo(`OPCUAIf: Disconnected\n`);
|
|
221
250
|
}
|
|
222
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Connect to the OPCUA server with the given URL.
|
|
254
|
+
* @param {string} url - The URL of the OPCUA server, typical format: opc.tcp://ip-address:4840 .
|
|
255
|
+
*/
|
|
223
256
|
async Connect(url) {
|
|
224
257
|
let self = this
|
|
225
|
-
if (this.#client){
|
|
258
|
+
if (this.#client) {
|
|
226
259
|
await this.Disconnect()
|
|
227
260
|
}
|
|
228
|
-
|
|
261
|
+
|
|
229
262
|
this.#client = OPCUAClient.create({
|
|
230
263
|
applicationName: "NodeOPCUA-Client",
|
|
231
264
|
endpointMustExist: true,
|
|
@@ -242,7 +275,7 @@ export class OPCUAIf extends BaseIf {
|
|
|
242
275
|
defaultSecureTokenLifetime: 20000,
|
|
243
276
|
tokenRenewalInterval: 1000
|
|
244
277
|
});
|
|
245
|
-
|
|
278
|
+
|
|
246
279
|
|
|
247
280
|
this.#client.on("backoff", (retry, delay) => {
|
|
248
281
|
//if ((retry % 10) == 0)
|
|
@@ -267,9 +300,9 @@ export class OPCUAIf extends BaseIf {
|
|
|
267
300
|
this.#client.on("after_reconnection", (err) => {
|
|
268
301
|
self.LogInfo(`OPCUAIf: After Reconnection event => ${err}\n`);
|
|
269
302
|
});
|
|
270
|
-
this.#client.on("security_token_renewed", () => {
|
|
303
|
+
/*this.#client.on("security_token_renewed", () => {
|
|
271
304
|
self.LogDebug(`OPCUAIf: security_token_renewed\n`);
|
|
272
|
-
})
|
|
305
|
+
})*/
|
|
273
306
|
this.#client.on("lifetime_75", (token) => { })
|
|
274
307
|
|
|
275
308
|
this.LogInfo(`OPCUAIf: connecting client to ${url}\n`);//, this.#session.toString());
|
|
@@ -299,23 +332,46 @@ export class OPCUAIf extends BaseIf {
|
|
|
299
332
|
requestedPublishingInterval: this.#publishingInterval
|
|
300
333
|
});
|
|
301
334
|
|
|
302
|
-
this.LogInfo(`OPCUAIf: session created\n`)
|
|
303
|
-
this.LogInfo(`OPCUAIf: client\n`);
|
|
304
|
-
this.LogInfo(`OPCUAIf: subscription\n`);
|
|
335
|
+
this.LogInfo(`OPCUAIf: session created\n`);
|
|
336
|
+
// this.LogInfo(`OPCUAIf: client\n`);
|
|
337
|
+
// this.LogInfo(`OPCUAIf: subscription\n`);
|
|
305
338
|
this.#connected = true
|
|
306
339
|
this.#endpointurl = url
|
|
307
340
|
}
|
|
308
341
|
|
|
309
|
-
|
|
310
|
-
|
|
342
|
+
/**
|
|
343
|
+
* Check if the given nodeID is a valid OPCUA nodeID.
|
|
344
|
+
* A valid nodeID has the format: ns=<namespace index>;s=<identifier>
|
|
345
|
+
* where <namespace index> is a number and <identifier> is a string.
|
|
346
|
+
* @param {*} nodeID
|
|
347
|
+
* @returns
|
|
348
|
+
*/
|
|
349
|
+
IsNodeID(nodeID) {
|
|
350
|
+
let strNodeID = nodeID.toString()
|
|
351
|
+
// regex: ns=\d;s=.*
|
|
352
|
+
if (strNodeID.match(/^ns=\d+;s=.*$/)) {
|
|
353
|
+
return true
|
|
354
|
+
}
|
|
355
|
+
return false
|
|
356
|
+
}
|
|
311
357
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
358
|
+
/**
|
|
359
|
+
* Register a subscription for a nodeID.
|
|
360
|
+
* This method will create a monitored item for the given nodeID and return the monitored item ID.
|
|
361
|
+
* If the nodeID is already registered, it will return the existing monitored item ID.
|
|
362
|
+
* @param {*} nodeID
|
|
363
|
+
* @returns
|
|
364
|
+
*/
|
|
365
|
+
async Subscribe(unformattedNodeID, options = {},attribute) {
|
|
366
|
+
// Format the nodeID using the provided options
|
|
367
|
+
let nodeID = this.formatString(unformattedNodeID, options)
|
|
368
|
+
|
|
369
|
+
// install monitored item
|
|
370
|
+
const monitoredNodeIdList = Object.values(this.monitoreditems)
|
|
371
|
+
for (let i = 0; i < monitoredNodeIdList.length; i++) {
|
|
372
|
+
if (monitoredNodeIdList[i] == nodeID) {
|
|
317
373
|
// already registered => return itemid
|
|
318
|
-
return
|
|
374
|
+
return this.monitoreditems[i]
|
|
319
375
|
}
|
|
320
376
|
}
|
|
321
377
|
|
|
@@ -324,13 +380,13 @@ export class OPCUAIf extends BaseIf {
|
|
|
324
380
|
attributeId: AttributeIds.Value
|
|
325
381
|
};
|
|
326
382
|
const monitoringParameters = {
|
|
327
|
-
samplingInterval:
|
|
383
|
+
samplingInterval: this.#samplingInterval,
|
|
328
384
|
discardOldest: true,
|
|
329
385
|
queueSize: 10
|
|
330
386
|
};
|
|
331
387
|
|
|
332
388
|
if (!this.#sub) {
|
|
333
|
-
this.LogError(`OPCUAIf:
|
|
389
|
+
this.LogError(`OPCUAIf: no subscription - don't register monitored items $itemToMonitor\n`);
|
|
334
390
|
return
|
|
335
391
|
}
|
|
336
392
|
const monitoredItem = await this.#sub.monitor(itemToMonitor, monitoringParameters, TimestampsToReturn.Both);
|
|
@@ -338,14 +394,20 @@ export class OPCUAIf extends BaseIf {
|
|
|
338
394
|
var self = this
|
|
339
395
|
monitoredItem.on("changed", function (dataValue) {
|
|
340
396
|
var nodeId = self.monitoreditems[this.monitoredItemId]
|
|
341
|
-
var
|
|
397
|
+
var buttonInfo = self.buttons[this.monitoredItemId]
|
|
342
398
|
// store the type of a nodeid in local dictionary
|
|
343
399
|
self.types[nodeId] = dataValue.value.dataType
|
|
344
400
|
// publish the value to subscribers:
|
|
345
|
-
self.LogInfo(`OPCUAIf: monitored item changed: ${nodeId} => ${dataValue.value.value}\n`);
|
|
346
|
-
self.emit('monitored item changed', buttonID, nodeId, dataValue.value.value)
|
|
401
|
+
self.LogInfo(`OPCUAIf: monitored item changed: ${buttonInfo.buttonID} ${buttonInfo.attribute} ${nodeId} => ${dataValue.value.value}\n`);
|
|
402
|
+
self.emit('monitored item changed', buttonInfo.buttonID, buttonInfo.attribute, nodeId, dataValue.value.value)
|
|
347
403
|
});
|
|
348
404
|
|
|
405
|
+
if (monitoredItem.monitoredItemId === undefined) {
|
|
406
|
+
this.LogError(`OPCUAIf: Error subscribing to node ${nodeID} (attribute ${attribute})\n`)
|
|
407
|
+
} else {
|
|
408
|
+
this.LogDebug(`OPCUAIf: Subscribe to ${monitoredItem.monitoredItemId},${nodeID},(attribute ${attribute})\n`);
|
|
409
|
+
}
|
|
410
|
+
|
|
349
411
|
return monitoredItem.monitoredItemId;
|
|
350
412
|
}
|
|
351
413
|
|
|
@@ -390,12 +452,10 @@ export class OPCUAIf extends BaseIf {
|
|
|
390
452
|
}
|
|
391
453
|
} else {
|
|
392
454
|
self.LogError("OPCUAIf: write NOT ok", nodeID, value, "\n");
|
|
393
|
-
self.LogError(err)
|
|
394
455
|
}
|
|
395
456
|
});
|
|
396
457
|
} catch (err) {
|
|
397
458
|
self.LogError("OPCUAIf: write NOT ok", nodeID, value, "\n");
|
|
398
|
-
self.LogError(err)
|
|
399
459
|
}
|
|
400
460
|
}
|
|
401
461
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loupedeck-commander",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
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": {
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"loupedeck": "^7.0.0",
|
|
15
15
|
"mkdirp": "^3.0.1",
|
|
16
16
|
"node-opcua": "^2.153.0",
|
|
17
|
-
"string-template": "^1.0.0"
|
|
17
|
+
"string-template": "^1.0.0",
|
|
18
|
+
"yaml": "^2.8.0"
|
|
18
19
|
},
|
|
19
20
|
"author": "Thomas Schneider",
|
|
20
21
|
"license": "Apache-2.0",
|
package/profile-1.yaml
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
name: profile-1
|
|
2
|
+
profile: example
|
|
3
|
+
description: ""
|
|
4
|
+
touch:
|
|
5
|
+
center:
|
|
6
|
+
"0":
|
|
7
|
+
states:
|
|
8
|
+
off:
|
|
9
|
+
color: "#000055"
|
|
10
|
+
textColor: "#FFFFFF"
|
|
11
|
+
image: icons/home.png
|
|
12
|
+
cmd: echo "{id} {state}"
|
|
13
|
+
font: 20px Arial
|
|
14
|
+
blink: true
|
|
15
|
+
on:
|
|
16
|
+
color: "#00ff00"
|
|
17
|
+
image: icons/home.png
|
|
18
|
+
cmd: echo "{id} {state}"
|
|
19
|
+
font: 10px Arial
|
|
20
|
+
blink: false
|
|
21
|
+
params:
|
|
22
|
+
text: "{key}"
|
|
23
|
+
"1":
|
|
24
|
+
states:
|
|
25
|
+
off:
|
|
26
|
+
color: "#000099"
|
|
27
|
+
image: icons/home.png
|
|
28
|
+
cmd: echo "{id} {state} {pressed}"
|
|
29
|
+
on:
|
|
30
|
+
color: "#00ff00"
|
|
31
|
+
image: icons/home.png
|
|
32
|
+
cmd: echo "{id} {state} {pressed}"
|
|
33
|
+
blink: true
|
|
34
|
+
params:
|
|
35
|
+
minPressed: 100
|
|
36
|
+
text: "{released}"
|
|
37
|
+
"2":
|
|
38
|
+
states:
|
|
39
|
+
off:
|
|
40
|
+
color: "#000099"
|
|
41
|
+
image: icons/home.png
|
|
42
|
+
cmd: echo "{id} {state}"
|
|
43
|
+
on:
|
|
44
|
+
color: "#00ff00"
|
|
45
|
+
image: icons/home.png
|
|
46
|
+
cmd: echo "{id} {state}"
|
|
47
|
+
"3":
|
|
48
|
+
states:
|
|
49
|
+
off:
|
|
50
|
+
color: "#000099"
|
|
51
|
+
image: icons/home.png
|
|
52
|
+
cmd: echo "{id} {state}"
|
|
53
|
+
on:
|
|
54
|
+
color: "#00ff00"
|
|
55
|
+
image: icons/home.png
|
|
56
|
+
cmd: echo "{id} {state}"
|
|
57
|
+
"4":
|
|
58
|
+
states:
|
|
59
|
+
off:
|
|
60
|
+
color: "#000099"
|
|
61
|
+
image: icons/home.png
|
|
62
|
+
cmd: echo "{id} {state}"
|
|
63
|
+
on:
|
|
64
|
+
color: "#00ff00"
|
|
65
|
+
image: icons/home.png
|
|
66
|
+
cmd: echo "{id} {state}"
|
|
67
|
+
"5":
|
|
68
|
+
states:
|
|
69
|
+
off:
|
|
70
|
+
color: "#000099"
|
|
71
|
+
image: icons/home.png
|
|
72
|
+
cmd: echo "{id} {state}"
|
|
73
|
+
on:
|
|
74
|
+
color: "#00ff00"
|
|
75
|
+
image: icons/home.png
|
|
76
|
+
cmd: echo "{id} {state}"
|
|
77
|
+
"6":
|
|
78
|
+
states:
|
|
79
|
+
off:
|
|
80
|
+
color: "#000099"
|
|
81
|
+
image: icons/home.png
|
|
82
|
+
cmd: echo "{id} {state}"
|
|
83
|
+
on:
|
|
84
|
+
color: "#00ff00"
|
|
85
|
+
image: icons/home.png
|
|
86
|
+
cmd: echo "{id} {state}"
|
|
87
|
+
"7":
|
|
88
|
+
states:
|
|
89
|
+
off:
|
|
90
|
+
color: "#000099"
|
|
91
|
+
image: icons/home.png
|
|
92
|
+
cmd: echo "{id} {state}"
|
|
93
|
+
on:
|
|
94
|
+
color: "#00ff00"
|
|
95
|
+
image: icons/home.png
|
|
96
|
+
cmd: echo "{id} {state}"
|
|
97
|
+
"8":
|
|
98
|
+
states:
|
|
99
|
+
off:
|
|
100
|
+
color: "#000099"
|
|
101
|
+
image: icons/home.png
|
|
102
|
+
cmd: echo "{id} {state}"
|
|
103
|
+
on:
|
|
104
|
+
color: "#00ff00"
|
|
105
|
+
image: icons/home.png
|
|
106
|
+
cmd: echo "{id} {state}"
|
|
107
|
+
"9":
|
|
108
|
+
states:
|
|
109
|
+
off:
|
|
110
|
+
color: "#000099"
|
|
111
|
+
image: icons/home.png
|
|
112
|
+
cmd: echo "{id} {state}"
|
|
113
|
+
on:
|
|
114
|
+
color: "#00ff00"
|
|
115
|
+
image: icons/home.png
|
|
116
|
+
cmd: echo "{id} {state}"
|
|
117
|
+
"10":
|
|
118
|
+
states:
|
|
119
|
+
off:
|
|
120
|
+
color: "#000099"
|
|
121
|
+
image: icons/home.png
|
|
122
|
+
cmd: echo "{id} {state}"
|
|
123
|
+
on:
|
|
124
|
+
color: "#00ff00"
|
|
125
|
+
image: icons/home.png
|
|
126
|
+
cmd: echo "{id} {state}"
|
|
127
|
+
"11":
|
|
128
|
+
states:
|
|
129
|
+
off:
|
|
130
|
+
color: "#000099"
|
|
131
|
+
image: icons/home.png
|
|
132
|
+
cmd: echo "{id} {state}"
|
|
133
|
+
on:
|
|
134
|
+
color: "#00ff00"
|
|
135
|
+
image: icons/home.png
|
|
136
|
+
cmd: echo "{id} {state}"
|
|
137
|
+
left:
|
|
138
|
+
"0":
|
|
139
|
+
states:
|
|
140
|
+
on:
|
|
141
|
+
color: "#000000"
|
|
142
|
+
cmd: echo "{id} {state}"
|
|
143
|
+
right:
|
|
144
|
+
"0":
|
|
145
|
+
states:
|
|
146
|
+
on:
|
|
147
|
+
color: "#000000"
|
|
148
|
+
cmd: echo "{id} {state}"
|
|
149
|
+
knobs:
|
|
150
|
+
knobTL:
|
|
151
|
+
states:
|
|
152
|
+
on:
|
|
153
|
+
cmd: echo "{id} {state}"
|
|
154
|
+
knobCL:
|
|
155
|
+
states:
|
|
156
|
+
on:
|
|
157
|
+
cmd: echo "{id} {state}"
|
|
158
|
+
knobBL:
|
|
159
|
+
states:
|
|
160
|
+
on:
|
|
161
|
+
cmd: echo "{id} {state}"
|
|
162
|
+
opcua: ns=2;s=DRV.AAFHR.compOut.FeldTempGetriebeoel
|
|
163
|
+
blink: ns=2;s=DRV.AAFHR.compOut.Feststellbremse
|
|
164
|
+
knobTR:
|
|
165
|
+
states:
|
|
166
|
+
on:
|
|
167
|
+
cmd: echo "{id} {state}"
|
|
168
|
+
knobCR:
|
|
169
|
+
states:
|
|
170
|
+
on:
|
|
171
|
+
cmd: echo "{id} {state}"
|
|
172
|
+
knobBR:
|
|
173
|
+
states:
|
|
174
|
+
on:
|
|
175
|
+
cmd: echo "{id} {state}"
|
|
176
|
+
parameters:
|
|
177
|
+
hostname: 127.0.0.1
|
|
178
|
+
simnbr: "1"
|
|
179
|
+
endpointurl: opc.tcp://{hostname}:4840
|
|
180
|
+
publishingInterval: 200
|
|
181
|
+
nodeid: ns=0;s=nodeID
|
|
182
|
+
textColor: "#abcdff"
|
|
183
|
+
textBaseLine: top
|
|
184
|
+
textAlign: left
|
|
185
|
+
font: 14px Arial
|
|
186
|
+
verbose: true
|