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.InputGainA.parameters[0].toFixed(1) %>db
+
+ |
+ + |
| + | + + | +
+
+ <%= data.oca.InputGainB.parameters[0].toFixed(1) %>db
+
+ |
+ + |
| + | + + | +
+
+ <%= data.oca.InputGainC.parameters[0].toFixed(1) %>db
+
+ |
+ + |
| + | + + | +
+
+ <%= data.oca.InputGainD.parameters[0].toFixed(1) %>db
+
+ |
+ + |