diff --git a/plugins/sennheiser/channel.js b/plugins/sennheiser/channel.js
new file mode 100644
index 0000000..ce03de9
--- /dev/null
+++ b/plugins/sennheiser/channel.js
@@ -0,0 +1,95 @@
+class Channel {
+ constructor() {
+ // common attributes
+ this.frequency = {
+ hz: null,
+ bank: null,
+ channel: null,
+ };
+ this.rfConfig = {
+ min: null, // hz
+ max: null, // hz
+ step: null, // hz
+ };
+ this.name = '';
+ this.mute = null; // 0 = disabled, 1 = enabled
+ this.firmwareRevision = ''; // #.#.#
+
+ this.msg = []; // list of msg strings (Ok, Low Battery, Low RF Signal, etc.)
+ this.configIndex = null;
+
+ // EM only
+ this.em = {
+ squelch: null,
+ afOut: null,
+ equalizer: null, // 0 = flat, 1 = low cut, 2 = low&high boost, 3 = high boost
+ rf1: {
+ min: null, // %
+ max: null, // %
+ active: null, // 0 = inactive, 1 = active
+ },
+ rf2: {
+ min: null, // %
+ max: null, // %
+ active: null, // 0 = inactive, 1 = active
+ },
+ states: {
+ mutes: {
+ // 4 bit number 1 bit per flag 0 = disabled, 1 = enabled
+ mute: null, // bit 0
+ txMute: null, // bit 1
+ rfMute: null, // bit 2
+ rxMute: null, // bit 3
+ },
+ pilot: 0, // 0 = no pilot for cycle, 1 = pilot for whole cycle, 2 = pilot signle for part of cycle
+ },
+ rf: {
+ level: 0, // %
+ antenna: null, // 1 or 2
+ pilot: null, // 0 = no pilot, 1 = pilot
+ },
+ af: {
+ peak: 0, // %
+ peakHold: 0, // %
+ mute: {
+ // 4 bit number 1 bit per flag
+ mute: null,
+ txMute: null,
+ rfMute: null,
+ rxMute: null,
+ },
+ },
+ bat: null, // % 0,30,70,100
+ };
+
+ // SR Only
+ this.sr = {
+ sensitivity: null, // -42..0
+ equalizer: {
+ enabled: null, // 0=off,1=on
+ low: null, // -5..5
+ lowMid: null, // -5..5
+ mid: null, // -5..5
+ midHigh: null, // -5..5
+ high: null, // -5..5
+ },
+ mode: null, // 0 = mono, 1 = stereo
+ af: [
+ {
+ peak: 0, // %
+ peakHold: 0, // %
+ },
+ {
+ peak: 0, // %
+ peakHold: 0, // %
+ },
+ ],
+ states: {
+ mute: null, // 0 = on, 1 = off
+ muteFlag: null, // 0 = rf mute on whole cycle, 1 = rf mute off whole cycle, 2 = rf mute changed during cycle
+ },
+ };
+ }
+}
+
+module.exports = Channel;
diff --git a/plugins/sennheiser/icon.png b/plugins/sennheiser/icon.png
new file mode 100644
index 0000000..2c0d01e
Binary files /dev/null and b/plugins/sennheiser/icon.png differ
diff --git a/plugins/sennheiser/info.html b/plugins/sennheiser/info.html
new file mode 100644
index 0000000..e69de29
diff --git a/plugins/sennheiser/main.js b/plugins/sennheiser/main.js
new file mode 100644
index 0000000..e625df3
--- /dev/null
+++ b/plugins/sennheiser/main.js
@@ -0,0 +1,40 @@
+const Channel = require('./channel');
+
+exports.config = {
+ defaultName: 'Sennheiser Wireless',
+ connectionType: 'UDPsocket',
+ heartbeatInterval: 5000,
+ heartbeatTimeout: 10000,
+ defaultPort: 53212,
+ mayChangePort: false,
+ searchOptions: {
+ type: 'UDPsocket',
+ searchBuffer: Buffer.from('Name\r', 'ascii'),
+ testPort: 53212,
+ validateResponse(msg, info) {
+ return msg.toString().includes('Name');
+ },
+ },
+};
+
+exports.ready = function ready(_device) {
+ const device = _device;
+
+ // send an immediate request for both types of attributes
+ _device.send('Push 0 0 1');
+ _device.send('Push 0 0 2');
+
+ device.data.channel = new Channel();
+};
+
+exports.data = function data(_device, message) {
+ console.log(message);
+ // Parse return message
+};
+
+exports.update = function update(device, doc, updateType, data) {};
+
+exports.heartbeat = function heartbeat(device) {
+ // tell unit to send both attribute updates every 500ms for 5 seconds
+ device.send('Push 5 500 3');
+};
diff --git a/plugins/sennheiser/styles.css b/plugins/sennheiser/styles.css
new file mode 100644
index 0000000..c1b63b2
--- /dev/null
+++ b/plugins/sennheiser/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: white !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/sennheiser/template.ejs b/plugins/sennheiser/template.ejs
new file mode 100644
index 0000000..1277fe8
--- /dev/null
+++ b/plugins/sennheiser/template.ejs
@@ -0,0 +1,52 @@
+<%= listName %>
+ <%= data.channel.firmwareRevision %>
+
| <%= data.channel.name %> | +||
|
+
+ 1
+ 2
+
+ rf
+
+
+ TODO+ dB + + |
+
+ + dB + + |
+ |
|
+
+
+
+
+ |
+ ||
| + TODO [msg and states] + | +||