diff --git a/plugins/dbaudio/icon.png b/plugins/dbaudio/icon.png new file mode 100644 index 0000000..8577e1e Binary files /dev/null and b/plugins/dbaudio/icon.png differ diff --git a/plugins/dbaudio/info.html b/plugins/dbaudio/info.html new file mode 100644 index 0000000..e69de29 diff --git a/plugins/dbaudio/main.js b/plugins/dbaudio/main.js new file mode 100644 index 0000000..df24740 --- /dev/null +++ b/plugins/dbaudio/main.js @@ -0,0 +1,439 @@ +exports.config = { + defaultName: 'd&b Amps', + connectionType: 'TCPsocket', + defaultPort: 30013, + mayChangePort: false, + heartbeatInterval: 1000, + heartbeatTimeout: 5000, + searchOptions: { + type: 'Bonjour', + bonjourName: 'oca', + }, +}; + +let ocaIDs = []; + +exports.ready = function ready(_device) { + const device = _device; + + device.data.oca = { + ModelDescription: { + target: 0x1000010d, + method: 0x00050001, + format: 'string', + }, + PwrOn: { + target: 0x10000100, + method: 0x00040001, + format: 'int16', + }, + SubNet: { + target: 0x10000109, + method: 0x00050001, + format: 'int32', + }, + DeviceId: { + target: 0x10000108, + method: 0x00050001, + format: 'int32', + }, + DeviceType: { + target: 0x10000034, + method: 0x00050001, + format: 'string', + }, + FirmwareRevision: { + target: 0x10000031, + method: 0x00050001, + format: 'string', + }, + ChannelNameA: { + target: 0x10008215, + method: 0x00050001, + format: 'string', + }, + ChannelNameB: { + target: 0x10010215, + method: 0x00050001, + format: 'string', + }, + ChannelNameC: { + target: 0x10018215, + method: 0x00050001, + format: 'string', + }, + ChannelNameD: { + target: 0x10020215, + method: 0x00050001, + format: 'string', + }, + DelayA: { + target: 0x10008207, + method: 0x00050001, + format: 'float', + }, + DelayB: { + target: 0x10010207, + method: 0x00050001, + format: 'float', + }, + DelayC: { + target: 0x10018207, + method: 0x00050001, + format: 'float', + }, + DelayD: { + target: 0x10020207, + method: 0x00050001, + format: 'float', + }, + InputEnable1A: { + target: 0x10008220, + method: 0x00040001, + format: 'int16', + }, + InputEnable2A: { + target: 0x10008221, + method: 0x00040001, + format: 'int16', + }, + InputEnable3A: { + target: 0x10008222, + method: 0x00040001, + format: 'int16', + }, + InputEnable4A: { + target: 0x10008223, + method: 0x00040001, + format: 'int16', + }, + InputEnable5A: { + target: 0x10008224, + method: 0x00040001, + format: 'int16', + }, + InputEnable6A: { + target: 0x10008225, + method: 0x00040001, + format: 'int16', + }, + InputEnable7A: { + target: 0x10008226, + method: 0x00040001, + format: 'int16', + }, + InputEnable8A: { + target: 0x10008227, + method: 0x00040001, + format: 'int16', + }, + InputEnable1B: { + target: 0x10010220, + method: 0x00040001, + format: 'int16', + }, + InputEnable2B: { + target: 0x10010221, + method: 0x00040001, + format: 'int16', + }, + InputEnable3B: { + target: 0x10010222, + method: 0x00040001, + format: 'int16', + }, + InputEnable4B: { + target: 0x10010223, + method: 0x00040001, + format: 'int16', + }, + InputEnable5B: { + target: 0x10010224, + method: 0x00040001, + format: 'int16', + }, + InputEnable6B: { + target: 0x10010225, + method: 0x00040001, + format: 'int16', + }, + InputEnable7B: { + target: 0x10010226, + method: 0x00040001, + format: 'int16', + }, + InputEnable8B: { + target: 0x10010227, + method: 0x00040001, + format: 'int16', + }, + InputEnable1C: { + target: 0x10018220, + method: 0x00040001, + format: 'int16', + }, + InputEnable2C: { + target: 0x10018221, + method: 0x00040001, + format: 'int16', + }, + InputEnable3C: { + target: 0x10018222, + method: 0x00040001, + format: 'int16', + }, + InputEnable4C: { + target: 0x10018223, + method: 0x00040001, + format: 'int16', + }, + InputEnable5C: { + target: 0x10018224, + method: 0x00040001, + format: 'int16', + }, + InputEnable6C: { + target: 0x10018225, + method: 0x00040001, + format: 'int16', + }, + InputEnable7C: { + target: 0x10018226, + method: 0x00040001, + format: 'int16', + }, + InputEnable8C: { + target: 0x10018227, + method: 0x00040001, + format: 'int16', + }, + InputEnable1D: { + target: 0x10020220, + method: 0x00040001, + format: 'int16', + }, + InputEnable2D: { + target: 0x10020221, + method: 0x00040001, + format: 'int16', + }, + InputEnable3D: { + target: 0x10020222, + method: 0x00040001, + format: 'int16', + }, + InputEnable4D: { + target: 0x10020223, + method: 0x00040001, + format: 'int16', + }, + InputEnable5D: { + target: 0x10020224, + method: 0x00040001, + format: 'int16', + }, + InputEnable6D: { + target: 0x10020225, + method: 0x00040001, + format: 'int16', + }, + InputEnable7D: { + target: 0x10020226, + method: 0x00040001, + format: 'int16', + }, + InputEnable8D: { + target: 0x10020227, + method: 0x00040001, + format: 'int16', + }, + InputGainA: { + target: 0x10008206, + method: 0x00040001, + format: 'float', + }, + InputGainB: { + target: 0x10010206, + method: 0x00040001, + format: 'float', + }, + InputGainC: { + target: 0x10018206, + method: 0x00040001, + format: 'float', + }, + InputGainD: { + target: 0x10020206, + method: 0x00040001, + format: 'float', + }, + SpeakerIDA: { + target: 0x10008214, + method: 0x00040001, + format: 'int16', + }, + SpeakerIDB: { + target: 0x10010214, + method: 0x00040001, + format: 'int16', + }, + SpeakerIDC: { + target: 0x10018214, + method: 0x00040001, + format: 'int16', + }, + SpeakerIDD: { + target: 0x10020214, + method: 0x00040001, + format: 'int16', + }, + SpeakerIDList: { + target: 0x10010214, + method: 0x00040005, + format: 'stringArray', + }, + OutputMode: { + target: 0x10000111, + method: 0x00040001, + format: 'int16', + }, + OutputModeList: { + target: 0x10000111, + method: 0x00040005, + format: 'stringArray', + }, + MuteA: { + target: 0x10008205, + method: 0x00040001, + format: 'int8', + }, + MuteB: { + target: 0x10010205, + method: 0x00040001, + format: 'int8', + }, + MuteC: { + target: 0x10018205, + method: 0x00040001, + format: 'int8', + }, + MuteD: { + target: 0x10020205, + method: 0x00040001, + format: 'int8', + }, + }; + + const ocaKeys = Object.keys(device.data.oca); + ocaIDs = []; + for (let i = 0; i < ocaKeys.length; i++) { + const key = device.data.oca[ocaKeys[i]]; + key.parameters = [0]; + ocaIDs[i] = key; + ocaCommandRequest(i, key.target, key.method, device); + ocaSubscriptionRequest(i, key.target, key.method, device); + } + console.log(device.data.oca); +}; + +exports.data = function data(_device, _buffer) { + if (_buffer[0] === 0x3b && _buffer[1] === 0 && _buffer[2] === 1) { + let start = 0; + while (start < _buffer.length - 3) { + const messageLength = _buffer.readInt32BE(start + 3) + 1; + const message = _buffer.slice(start, start + messageLength); + + const messageType = message[7]; + + if (messageType === 0x04) { + // keep-alive message + } else if (messageType === 0x02) { + // console.log('NOTIFICATION!!'); + console.log(message.slice(33)); + } else if (messageType === 0x03) { + const messageHandle = _buffer.readInt32BE(start + 14); + const paramCount = message[19]; + + if (paramCount) { + let paramsStart = 20; + + ocaIDs[messageHandle].parameters = []; + + while (paramsStart < message.length) { + let paramLength = 0; + let param; + + if ( + ocaIDs[messageHandle].format === 'int16' || + ocaIDs[messageHandle].format === 'float' || + ocaIDs[messageHandle].format === 'int32' + ) { + paramLength = 2; + param = message.slice(paramsStart, paramsStart + paramLength + 2); + } else if (ocaIDs[messageHandle].format === 'int8') { + paramLength = 1; + param = message.slice(paramsStart, paramsStart + paramLength); + } else if (ocaIDs[messageHandle].format === 'stringArray') { + const nextBreak = message.indexOf(0x00, paramsStart + 1); + + if (nextBreak < 0) { + break; + } + + paramLength = nextBreak - paramsStart + 1; + param = message.slice(paramsStart - 1, paramsStart + paramLength - 1).toString(); + } else { + paramLength = message.readInt16BE(20); + param = message.slice(paramsStart + 2, paramsStart + 2 + paramLength); + } + + if (ocaIDs[messageHandle].format === 'string') { + ocaIDs[messageHandle].parameters.push(param.toString()); + } else if (ocaIDs[messageHandle].format === 'stringArray') { + ocaIDs[messageHandle].parameters.push(param.toString()); + } else if (ocaIDs[messageHandle].format === 'float') { + ocaIDs[messageHandle].parameters.push(param.readFloatBE(0)); + } else if (ocaIDs[messageHandle].format === 'int16') { + ocaIDs[messageHandle].parameters.push(param.readInt16BE(0)); + } else if (ocaIDs[messageHandle].format === 'int8') { + ocaIDs[messageHandle].parameters.push(param.readInt8(0)); + } else if (ocaIDs[messageHandle].format === 'int32') { + ocaIDs[messageHandle].parameters.push(param.readInt32BE(0)); + } else { + ocaIDs[messageHandle].parameters.push(param); + } + + paramsStart += paramLength + 2; + } + } + } + + start += messageLength; + } + } +}; + +exports.heartbeat = function heartbeat(device) { + device.send(Buffer.from('3b00010000000b0400010001', 'hex')); + device.draw(); +}; + +function ocaCommandRequest(handle, target, method, device) { + const buf = Buffer.from('3b00010000001a01000100000011AAAAAAAABBBBBBBBCCCCCCCC00', 'hex'); + buf.writeInt32BE(handle, 14); // handle + buf.writeInt32BE(target, 18); // target + buf.writeInt32BE(method, 22); // method + device.send(buf); + return buf; +} + +function ocaSubscriptionRequest(handle, target, method, device) { + const buf = Buffer.from( + '3b00010000002f01000100000026000000ae00000004000300010510008205000100010000041f000100010000010000', + 'hex' + ); + buf.writeInt32BE(handle, 14); // handle + buf.writeInt32BE(target, 27); // target + device.send(buf); + return buf; +} diff --git a/plugins/dbaudio/muteicon-mute.png b/plugins/dbaudio/muteicon-mute.png new file mode 100644 index 0000000..3846e11 Binary files /dev/null and b/plugins/dbaudio/muteicon-mute.png differ diff --git a/plugins/dbaudio/muteicon-on.png b/plugins/dbaudio/muteicon-on.png new file mode 100644 index 0000000..c355101 Binary files /dev/null and b/plugins/dbaudio/muteicon-on.png differ diff --git a/plugins/dbaudio/muteicon.afdesign b/plugins/dbaudio/muteicon.afdesign new file mode 100644 index 0000000..8cf3a9b Binary files /dev/null and b/plugins/dbaudio/muteicon.afdesign differ diff --git a/plugins/dbaudio/styles.css b/plugins/dbaudio/styles.css new file mode 100644 index 0000000..40f71fe --- /dev/null +++ b/plugins/dbaudio/styles.css @@ -0,0 +1,96 @@ +table { + color: white; + background-color: #323232; + font-size: 14px; +} +.button { + padding: 4px; + height: 65px; + border-radius: 4px; + position: relative; + text-align: right; + margin: 2px; + box-sizing: border-box; + overflow: hidden; + white-space: nowrap; +} +.light-button { + background: linear-gradient(0deg, #484848, #737373); + outline: black 2px outset; + border-top: #8b8b8b 2px solid; + border-left: #8b8b8b 2px solid; +} +.dark-button { + background: linear-gradient(0deg, #0e0e0e, #282828); + outline: #737373 2px solid; + text-overflow: ellipsis; + white-space: nowrap; +} +.mute { + text-align: center; + background-image: url('muteicon-on.png'), linear-gradient(0deg, #484848, #737373); + background-position: 52px 13px, 0px 0px; + background-repeat: no-repeat; +} +.gain-box { + background-color: #272727; + height: 60px; + width: 65px; + margin: 2px; + border: #656565 2px solid; + outline: #111111 2px solid; + text-align: right; +} +.gain-head { + box-sizing: border-box; + height: 12px; + width: 100%; + background: #686868; + border: #1a1a1a 2px solid; + outline: #656565 2px solid; + margin-bottom: 27px; +} +.gain-box span { + padding: 4px; +} +h3 { + margin: 7px 0px 3px; + margin-left: 12px; + text-align: left; + font-weight: normal; + font-size: 22px; +} +h4 { + text-align: left; + font-size: 16px; + margin: 0px; +} + +.channel-input { + position: absolute; + left: 9px; + top: 27px; + border-right: white 1px solid; + padding-right: 40px; + text-align: left; + line-height: 14px; +} +.channel-delay { + position: absolute; + left: 170px; + top: 40px; +} +.power-1 { + background: linear-gradient(0deg, #5a8127, #789b50); + border: #506937 1px solid; +} +.indicate-1 { + background: #00ff00; + border: black 2px inset; +} + +.mute-1 { + border-top: #772b26 2px solid; + border-left: #772b26 2px solid; + background-image: url('muteicon-mute.png'), linear-gradient(0deg, #b52714, #c4473e); +} diff --git a/plugins/dbaudio/template.ejs b/plugins/dbaudio/template.ejs new file mode 100644 index 0000000..b3afd3a --- /dev/null +++ b/plugins/dbaudio/template.ejs @@ -0,0 +1,137 @@ +
+

<%= listName %>

+

<%= data.oca.FirmwareRevision.parameters[0] %>

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+

<%= data.oca.ModelDescription.parameters[0] %>


+ <%= data.oca.OutputModeList.parameters[data.oca.OutputMode.parameters[0]+1] %> +
+
+
+

ID


+ <%= data.oca.SubNet.parameters[0] %>.<%= data.oca.DeviceId.parameters[0].toString().padStart(2, '0') %> +
+

Power


On
+
+

<%= data.oca.ChannelNameA.parameters[0] %>

+
+ Input
+ <% if(data.oca.InputEnable1A.parameters[0]){ %> A1 <% } %> + <% if(data.oca.InputEnable2A.parameters[0]){ %> A2 <% } %> + <% if(data.oca.InputEnable3A.parameters[0]){ %> A3 <% } %> + <% if(data.oca.InputEnable4A.parameters[0]){ %> A4 <% } %> + <% if(data.oca.InputEnable5A.parameters[0]){ %> D1 <% } %> + <% if(data.oca.InputEnable6A.parameters[0]){ %> D2 <% } %> + <% if(data.oca.InputEnable7A.parameters[0]){ %> D3 <% } %> + <% if(data.oca.InputEnable8A.parameters[0]){ %> D4 <% } %> +
+
+ <%= data.oca.DelayA.parameters[0].toFixed(1) %>ms +
+
+
+
+ <%= data.oca.InputGainA.parameters[0].toFixed(1) %>db +
+

A

<%= data.oca.SpeakerIDList.parameters[data.oca.SpeakerIDA.parameters[0]] %>
+
+

<%= data.oca.ChannelNameB.parameters[0] %>

+
+ Input
+ <% if(data.oca.InputEnable1B.parameters[0]){ %> A1 <% } %> + <% if(data.oca.InputEnable2B.parameters[0]){ %> A2 <% } %> + <% if(data.oca.InputEnable3B.parameters[0]){ %> A3 <% } %> + <% if(data.oca.InputEnable4B.parameters[0]){ %> A4 <% } %> + <% if(data.oca.InputEnable5B.parameters[0]){ %> D1 <% } %> + <% if(data.oca.InputEnable6B.parameters[0]){ %> D2 <% } %> + <% if(data.oca.InputEnable7B.parameters[0]){ %> D3 <% } %> + <% if(data.oca.InputEnable8B.parameters[0]){ %> D4 <% } %> +
+
+ <%= data.oca.DelayB.parameters[0].toFixed(1) %>ms +
+
+
+
+ <%= data.oca.InputGainB.parameters[0].toFixed(1) %>db +
+

B

<%= data.oca.SpeakerIDList.parameters[data.oca.SpeakerIDB.parameters[0]] %>
+
+

<%= data.oca.ChannelNameC.parameters[0] %>

+
+ Input
+ <% if(data.oca.InputEnable1C.parameters[0]){ %> A1 <% } %> + <% if(data.oca.InputEnable2C.parameters[0]){ %> A2 <% } %> + <% if(data.oca.InputEnable3C.parameters[0]){ %> A3 <% } %> + <% if(data.oca.InputEnable4C.parameters[0]){ %> A4 <% } %> + <% if(data.oca.InputEnable5C.parameters[0]){ %> D1 <% } %> + <% if(data.oca.InputEnable6C.parameters[0]){ %> D2 <% } %> + <% if(data.oca.InputEnable7C.parameters[0]){ %> D3 <% } %> + <% if(data.oca.InputEnable8C.parameters[0]){ %> D4 <% } %> +
+
+ <%= data.oca.DelayC.parameters[0].toFixed(1) %>ms +
+
+
+
+ <%= data.oca.InputGainC.parameters[0].toFixed(1) %>db +
+

C

<%= data.oca.SpeakerIDList.parameters[data.oca.SpeakerIDC.parameters[0]] %>
+
+

<%= data.oca.ChannelNameD.parameters[0] %>

+
+ Input
+ <% if(data.oca.InputEnable1D.parameters[0]){ %> A1 <% } %> + <% if(data.oca.InputEnable2D.parameters[0]){ %> A2 <% } %> + <% if(data.oca.InputEnable3D.parameters[0]){ %> A3 <% } %> + <% if(data.oca.InputEnable4D.parameters[0]){ %> A4 <% } %> + <% if(data.oca.InputEnable5D.parameters[0]){ %> D1 <% } %> + <% if(data.oca.InputEnable6D.parameters[0]){ %> D2 <% } %> + <% if(data.oca.InputEnable7D.parameters[0]){ %> D3 <% } %> + <% if(data.oca.InputEnable8D.parameters[0]){ %> D4 <% } %> +
+
+ <%= data.oca.DelayD.parameters[0].toFixed(1) %>ms +
+
+
+
+ <%= data.oca.InputGainD.parameters[0].toFixed(1) %>db +
+

D

<%= data.oca.SpeakerIDList.parameters[data.oca.SpeakerIDD.parameters[0]] %>