diff --git a/.vscode/settings.json b/.vscode/settings.json
index eb83d0e..3c1a1bb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,5 +4,6 @@
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
- "eslint.validate": ["javascript"]
+ "eslint.validate": ["javascript"],
+ "html.validate.styles": false // accept templates inside HTML style attributes
}
diff --git a/plugins/shure/channel.js b/plugins/shure/channel.js
new file mode 100644
index 0000000..dbdaf05
--- /dev/null
+++ b/plugins/shure/channel.js
@@ -0,0 +1,14 @@
+class Channel {
+ constructor() {
+ this.chan_name = '?';
+ this.batt_bars = 255;
+ this.batt_run_time = 65535;
+ this.audio_gain = 0;
+ this.audio_lvl = 0;
+ this.rx_rf_lvl = 0;
+ this.rf_antenna = 0;
+ this.tx_type = 0;
+ }
+}
+
+module.exports = Channel;
diff --git a/plugins/shure/icon.png b/plugins/shure/icon.png
new file mode 100644
index 0000000..1ef31f0
Binary files /dev/null and b/plugins/shure/icon.png differ
diff --git a/plugins/shure/info.html b/plugins/shure/info.html
new file mode 100644
index 0000000..e69de29
diff --git a/plugins/shure/main.js b/plugins/shure/main.js
new file mode 100644
index 0000000..047411c
--- /dev/null
+++ b/plugins/shure/main.js
@@ -0,0 +1,128 @@
+const Channel = require('./channel');
+
+exports.config = {
+ defaultName: 'Shure Wireless',
+ connectionType: 'TCPsocket',
+ heartbeatInterval: 30000,
+ heartbeatTimeout: 35000,
+ defaultPort: 2202,
+ mayChangePort: false,
+ searchOptions: {
+ type: 'TCPport',
+ searchBuffer: Buffer.from('< GET DEVICE_ID >', 'ascii'),
+ testPort: 3032,
+ validateResponse(msg, info) {
+ return msg.toString().indexOf('DEVICE_ID');
+ },
+ },
+};
+
+exports.ready = function ready(_device) {
+ const device = _device;
+ device.data.channelCount = 0;
+ device.data.channels = [
+ {},
+ new Channel(),
+ new Channel(),
+ new Channel(),
+ new Channel(),
+ ];
+};
+
+exports.data = function data(_device, message) {
+ const device = _device;
+ let msgStr = message.toString();
+
+ if (!msgStr.startsWith('< ')) {
+ return;
+ }
+
+ msgStr = msgStr.slice(2).slice(0, -1);
+ const msgs = msgStr.split('><');
+
+ msgs.forEach((ms, i) => {
+ const msg = ms.trim();
+ const msgParts = msg.split(' ');
+ const channelNumber = Number(msgParts[1]);
+ const channel = device.data.channels[channelNumber];
+
+ if (msgParts[0] === 'REP') {
+ if (msgParts[2] === 'CHAN_NAME') {
+ channel.chan_name = msg.substring(17).slice(0, -2).trim();
+ if (device.data.channelCount < channelNumber) {
+ device.data.channelCount = channelNumber;
+ }
+ device.draw();
+ } else if (msgParts[2] === 'BATT_BARS') {
+ channel.batt_bars = Number(msgParts[3]);
+ } else if (msgParts[2] === 'BATT_RUN_TIME') {
+ channel.batt_run_time = Number(msgParts[3]);
+ } else if (msgParts[2] === 'AUDIO_GAIN') {
+ channel.audio_gain = Number(msgParts[3]) - 18;
+ } else if (msgParts[2] === 'AUDIO_LVL') {
+ channel.audio_lvl = Number(msgParts[3]);
+ } else if (msgParts[2] === 'RX_RF_LVL') {
+ channel.rx_rf_lvl = Number(msgParts[3]) - 128;
+ } else if (msgParts[2] === 'RF_ANTENNA') {
+ channel.rf_antenna = msgParts[3];
+ } else if (msgParts[2] === 'TX_TYPE') {
+ channel.tx_type = msgParts[3];
+ device.draw();
+ } else if (msgParts[1] === 'DEVICE_ID') {
+ const id = msg.substring(15).slice(0, -1).trim();
+ this.deviceInfoUpdate(device, 'defaultName', id);
+ } else if (msgParts[1] === 'FW_VER') {
+ device.data.version = msgParts[2].substring(1);
+ }
+ } else if (msgParts[0] === 'SAMPLE') {
+ channel.rf_antenna = msgParts[3];
+ channel.rx_rf_lvl = Number(msgParts[4]) - 128;
+ channel.audio_lvl = Number(msgParts[5]);
+ if (channelNumber === 4) {
+ device.update('updateSample', {
+ channels: device.data.channels,
+ });
+ }
+ }
+ });
+};
+
+exports.update = function update(device, doc, updateType, data) {
+ for (let i = 1; i < data.channels.length; i++) {
+ const channel = data.channels[i];
+
+ const $rf = doc.getElementById(`ch-${i}-rf`);
+ if ($rf) {
+ $rf.style.height = 90 - (channel.rx_rf_lvl + 90) * 2;
+ }
+
+ const $audio = doc.getElementById(`ch-${i}-audio`);
+ if ($audio) {
+ $audio.style.height = 90 - channel.audio_lvl * 2;
+ }
+
+ const $rfA = doc.getElementById(`ch-${i}-a`);
+ const $rfB = doc.getElementById(`ch-${i}-b`);
+
+ if ($rfA) {
+ if (channel.rf_antenna.charAt(0) === 'A') {
+ $rfA.style.color = '#53c3c3';
+ } else {
+ $rfA.style.color = '#333';
+ }
+ }
+ if ($rfB) {
+ if (channel.rf_antenna.charAt(1) === 'B') {
+ $rfB.style.color = '#53c3c3';
+ } else {
+ $rfB.style.color = '#333';
+ }
+ }
+ }
+};
+
+exports.heartbeat = function heartbeat(device) {
+ device.send('< GET 0 ALL >');
+ device.send('< SET 0 METER_RATE 00100 >');
+ device.send('< SAMPLE 0 AUDIO_LVL>');
+};
diff --git a/plugins/shure/styles.css b/plugins/shure/styles.css
new file mode 100644
index 0000000..c7a46b0
--- /dev/null
+++ b/plugins/shure/styles.css
@@ -0,0 +1,101 @@
+table.channel {
+ width: 120px;
+ height: 320px;
+ background-color: #1a1a1d;
+ border: #3b3c42 1px solid;
+ border-collapse: collapse;
+ color: white;
+ text-align: center;
+ float: left;
+ margin: 2px;
+ margin-bottom: 20px;
+}
+table td {
+ border: #3b3c42 1px solid;
+ vertical-align: top;
+}
+
+.chan_name {
+ width: 100px;
+ color: #b2ff33 !important;
+ font-size: 18px;
+ font-weight: bold;
+}
+
+.bar-wrapper {
+ width: 20px;
+ height: 90px;
+ margin: 0px auto;
+ margin-top: 5px;
+ margin-bottom: 5px;
+
+ border-radius: 6px;
+ outline: #1a1a1d 2px solid;
+ outline-offset: -1px;
+ overflow: hidden;
+}
+.bar-wrapper.green {
+ background: linear-gradient(0deg, #4742af, #554ed4 30%, #554ed4 80%, #df664d);
+}
+.bar-wrapper.pink {
+ background: linear-gradient(0deg, #383943, #4e505d 40%);
+}
+.bar-wrapper.orange {
+ background: linear-gradient(0deg, #50c3c3, #5bdfe0 30%, #5bdfe0 80%);
+}
+.bar {
+ width: 20px;
+ background-color: #202124;
+ outline: rgba(255, 255, 255, 0.1) 1px solid;
+}
+
+.batt-wrapper {
+ width: 84px;
+ height: 35px;
+ margin: 0px auto;
+ border: #333 2px solid;
+ border-radius: 5px;
+ padding: 2px;
+ margin: 10px auto;
+ position: relative;
+}
+.batt-wrapper.green {
+ border-color: greenyellow;
+}
+.batt-knob {
+ background-color: #333;
+ position: absolute;
+ right: -5px;
+ top: 10px;
+ width: 5px;
+ height: 16px;
+ border-radius: 2px;
+}
+.batt-knob.green {
+ background-color: greenyellow;
+}
+.batt-bar {
+ height: 26px;
+ background: linear-gradient(0deg, rgb(145, 220, 33), greenyellow 100%);
+ border-radius: 2px;
+ color: #1a1a1d;
+ padding-top: 9px;
+}
+
+.rf-indicator-wrapper {
+ line-height: 8px;
+ margin-bottom: 4px;
+ font-size: 6px;
+ padding-top: 4px;
+ letter-spacing: 1px;
+}
+.rf-indicator {
+ color: #333;
+}
+
+small {
+ color: dimgray;
+}
+small small {
+ color: #444;
+}
diff --git a/plugins/shure/template.ejs b/plugins/shure/template.ejs
new file mode 100644
index 0000000..d989f75
--- /dev/null
+++ b/plugins/shure/template.ejs
@@ -0,0 +1,72 @@
+<%= listName %>
+ <%= data.version || "" %>
+
| <%= i %> | +||
| <%= ch.chan_name %> | +||
|
+
+ ⬤
+ ⬤
+
+ rf
+
+ <%= ch.rx_rf_lvl %>dBm + |
+
+ dBFS + |
+
+ dB + |
+
|
+ <% if(ch.batt_bars==255){ %>
+
+ <% }else{ %>
+
+
+
+
+ <% } %>
+ |
+ ||
| + <% if(ch.tx_type=="UNKN"){ %> No Transmitter <% + }else{ %> <%= ch.tx_type %> <% } %> + | +||