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 || "" %>

+
+ +<% for(let i=1; i<=data.channelCount; i++){ %> <% let ch = data.channels[i]; %> + + + + + + + + + + + + + + + + + + +
<%= i %>
<%= ch.chan_name %>
+
+ + +
+ rf +
+
+
+ <%= ch.rx_rf_lvl %>
dBm
+
+
 
+ audio +
+
+
+ <%= ch.audio_lvl %>
dBFS
+
+
 
+ gain +
+
+
+ <%= ch.audio_gain %>
dB
+
+ <% if(ch.batt_bars==255){ %> +
+ <% }else{ %> +
+
+ <% if(ch.batt_run_time<=65532){ %> <%= Math.floor(ch.batt_run_time/60) + %>: <%= ch.batt_run_time%60 %> <% } %> +
+
+
+ <% } %> +
+ <% if(ch.tx_type=="UNKN"){ %>  No Transmitter  <% + }else{ %> <%= ch.tx_type %> <% } %> +
+ +<% } %> diff --git a/src/device.js b/src/device.js index 6ae0446..a743c0f 100644 --- a/src/device.js +++ b/src/device.js @@ -223,7 +223,7 @@ module.exports.deleteActive = function deleteActive() { module.exports.changeActiveType = function changeActiveType(newType) { const device = VIEW.getActiveDevice(); device.type = newType; - device.fields = {}; + device.fields = []; device.plugin = PLUGINS.all[newType]; if (PLUGINS.all[device.type].config.fields) {