diff --git a/htdocs/generalsettings.html b/htdocs/generalsettings.html
index 3367ed8..4731a02 100644
--- a/htdocs/generalsettings.html
+++ b/htdocs/generalsettings.html
@@ -5,10 +5,8 @@
     
     
     
-    
-    
     
-    
+    
     
 
 
diff --git a/htdocs/lib/settings/Input.js b/htdocs/lib/settings/Input.js
new file mode 100644
index 0000000..4686aac
--- /dev/null
+++ b/htdocs/lib/settings/Input.js
@@ -0,0 +1,114 @@
+function Input(name, value, options) {
+    this.name = name;
+    this.value = value;
+    this.options = options;
+    this.label = options && options.label || name;
+};
+
+Input.prototype.getClasses = function() {
+    return ['form-control', 'form-control-sm'];
+}
+
+Input.prototype.bootstrapify = function(input) {
+    this.getClasses().forEach(input.addClass.bind(input));
+    return [
+        ''
+    ].join('');
+};
+
+function TextInput() {
+    Input.apply(this, arguments);
+};
+
+TextInput.prototype = new Input();
+
+TextInput.prototype.render = function() {
+    return this.bootstrapify($(''));
+}
+
+function NumberInput() {
+    Input.apply(this, arguments);
+};
+
+NumberInput.prototype = new Input();
+
+NumberInput.prototype.render = function() {
+    return this.bootstrapify($(''));
+};
+
+function SoapyGainInput() {
+    Input.apply(this, arguments);
+}
+
+SoapyGainInput.prototype = new Input();
+
+SoapyGainInput.prototype.getClasses = function() {
+    return [];
+};
+
+SoapyGainInput.prototype.render = function(){
+    var markup = $(
+        '' +
+        '' +
+        this.options.gains.map(function(g){
+            return '';
+        }).join('')
+    );
+    var el = $(this.bootstrapify(markup))
+    var setMode = function(mode){
+        el.find('.option').hide();
+        el.find('.gain-mode-' + mode).show();
+    };
+    el.on('change', 'select', function(){
+        var mode = $(this).val();
+        setMode(mode);
+    });
+    setMode('auto');
+    return el;
+};
+
+function ProfileInput() {
+    Input.apply(this, arguments);
+};
+
+ProfileInput.prototype = new Input();
+
+ProfileInput.prototype.render = function() {
+    return $('Profiles
');
+};
+
+function SchedulerInput() {
+    Input.apply(this, arguments);
+};
+
+SchedulerInput.prototype = new Input();
+
+SchedulerInput.prototype.render = function() {
+    return $('Scheduler
');
+};
diff --git a/htdocs/lib/settings/SdrDevice.js b/htdocs/lib/settings/SdrDevice.js
new file mode 100644
index 0000000..f8acada
--- /dev/null
+++ b/htdocs/lib/settings/SdrDevice.js
@@ -0,0 +1,226 @@
+function SdrDevice(el, data) {
+    this.el = el;
+    this.data = data;
+    this.inputs = {};
+    this.render();
+
+    var self = this;
+    el.on('click', '.fieldselector .btn', function() {
+        var key = el.find('.fieldselector select').val();
+        self.data[key] = self.getInitialValue(key);
+        self.render();
+    });
+};
+
+SdrDevice.create = function(el) {
+    var data = JSON.parse(decodeURIComponent(el.data('config')));
+    var type = data.type;
+    var constructor = SdrDevice.types[type] || SdrDevice;
+    return new constructor(el, data);
+};
+
+SdrDevice.prototype.getData = function() {
+    return $.extend(new Object(), this.getDefaults(), this.data);
+};
+
+SdrDevice.prototype.getDefaults = function() {
+    var defaults = {}
+    $.each(this.getMappings(), function(k, v) {
+        if (!v.includeInDefault) return;
+        defaults[k] = 'initialValue' in v ? v['initialValue'] : false;
+    });
+    return defaults;
+};
+
+SdrDevice.prototype.getMappings = function() {
+    return {
+        "name": {
+            constructor: TextInput,
+            inputOptions: {
+                label: "Name"
+            },
+            initialValue: "",
+            includeInDefault: true
+        },
+        "type": {
+            constructor: TextInput,
+            inputOptions: {
+                label: "Type"
+            },
+            initialValue: '',
+            includeInDefault: true
+        },
+        "ppm": {
+            constructor: NumberInput,
+            inputOptions: {
+                label: "PPM"
+            },
+            initialValue: 0
+        },
+        "profiles": {
+            constructor: ProfileInput,
+            inputOptions: {
+                label: "Profiles"
+            },
+            initialValue: [],
+            includeInDefault: true,
+            position: 100
+        },
+        "scheduler": {
+            constructor: SchedulerInput,
+            inputOptions: {
+                label: "Scheduler",
+            },
+            initialValue: {},
+            position: 101
+        },
+        "rf_gain": {
+            constructor: TextInput,
+            inputOptions: {
+                label: "Gain",
+            },
+            initialValue: 0
+        }
+    };
+};
+
+SdrDevice.prototype.getMapping = function(key) {
+    var mappings = this.getMappings();
+    return mappings[key];
+};
+
+SdrDevice.prototype.getInputClass = function(key) {
+    var mapping = this.getMapping(key);
+    return mapping && mapping.constructor || TextInput;
+};
+
+SdrDevice.prototype.getInitialValue = function(key) {
+    var mapping = this.getMapping(key);
+    return mapping && ('initialValue' in mapping) ? mapping['initialValue'] : false;
+};
+
+SdrDevice.prototype.getPosition = function(key) {
+    var mapping = this.getMapping(key);
+    return mapping && mapping.position || 10;
+};
+
+SdrDevice.prototype.getInputOptions = function(key) {
+    var mapping = this.getMapping(key);
+    return mapping && mapping.inputOptions || {};
+};
+
+SdrDevice.prototype.getLabel = function(key) {
+    var options = this.getInputOptions(key);
+    return options && options.label || key;
+};
+
+SdrDevice.prototype.render = function() {
+    var self = this;
+    self.el.empty();
+    var data = this.getData();
+    Object.keys(data).sort(function(a, b){
+        return self.getPosition(a) - self.getPosition(b);
+    }).forEach(function(key){
+        var value = data[key];
+        var inputClass = self.getInputClass(key);
+        var input = new inputClass(key, value, self.getInputOptions(key));
+        self.inputs[key] = input;
+        self.el.append(input.render());
+    });
+    self.el.append(this.renderFieldSelector());
+};
+
+SdrDevice.prototype.renderFieldSelector = function() {
+    var self = this;
+    return '' +
+        '
Add new configuration options' +
+        '' +
+    '
Profiles
');
-};
-
-function SchedulerInput() {
-    Input.apply(this, arguments);
-};
-
-SchedulerInput.prototype = new Input();
-
-SchedulerInput.prototype.render = function() {
-    return $('Scheduler
');
-};
-
-function SdrDevice(el, data) {
-    this.el = el;
-    this.data = data;
-    this.inputs = {};
-    this.render();
-
-    var self = this;
-    el.on('click', '.fieldselector .btn', function() {
-        var key = el.find('.fieldselector select').val();
-        self.data[key] = self.getInitialValue(key);
-        self.render();
-    });
-};
-
-SdrDevice.create = function(el) {
-    var data = JSON.parse(decodeURIComponent(el.data('config')));
-    var type = data.type;
-    var constructor = SdrDevice.types[type] || SdrDevice;
-    return new constructor(el, data);
-};
-
-SdrDevice.prototype.getData = function() {
-    return $.extend(new Object(), this.getDefaults(), this.data);
-};
-
-SdrDevice.prototype.getDefaults = function() {
-    var defaults = {}
-    $.each(this.getMappings(), function(k, v) {
-        if (!v.includeInDefault) return;
-        defaults[k] = 'initialValue' in v ? v['initialValue'] : false;
-    });
-    return defaults;
-};
-
-SdrDevice.prototype.getMappings = function() {
-    return {
-        "name": {
-            constructor: TextInput,
-            inputOptions: {
-                label: "Name"
-            },
-            initialValue: "",
-            includeInDefault: true
-        },
-        "type": {
-            constructor: TextInput,
-            inputOptions: {
-                label: "Type"
-            },
-            initialValue: '',
-            includeInDefault: true
-        },
-        "ppm": {
-            constructor: NumberInput,
-            inputOptions: {
-                label: "PPM"
-            },
-            initialValue: 0
-        },
-        "profiles": {
-            constructor: ProfileInput,
-            inputOptions: {
-                label: "Profiles"
-            },
-            initialValue: [],
-            includeInDefault: true,
-            position: 100
-        },
-        "scheduler": {
-            constructor: SchedulerInput,
-            inputOptions: {
-                label: "Scheduler",
-            },
-            initialValue: {},
-            position: 101
-        },
-        "rf_gain": {
-            constructor: TextInput,
-            inputOptions: {
-                label: "Gain",
-            },
-            initialValue: 0
-        }
-    };
-};
-
-SdrDevice.prototype.getMapping = function(key) {
-    var mappings = this.getMappings();
-    return mappings[key];
-};
-
-SdrDevice.prototype.getInputClass = function(key) {
-    var mapping = this.getMapping(key);
-    return mapping && mapping.constructor || TextInput;
-};
-
-SdrDevice.prototype.getInitialValue = function(key) {
-    var mapping = this.getMapping(key);
-    return mapping && ('initialValue' in mapping) ? mapping['initialValue'] : false;
-};
-
-SdrDevice.prototype.getPosition = function(key) {
-    var mapping = this.getMapping(key);
-    return mapping && mapping.position || 10;
-};
-
-SdrDevice.prototype.getInputOptions = function(key) {
-    var mapping = this.getMapping(key);
-    return mapping && mapping.inputOptions || {};
-};
-
-SdrDevice.prototype.getLabel = function(key) {
-    var options = this.getInputOptions(key);
-    return options && options.label || key;
-};
-
-SdrDevice.prototype.render = function() {
-    var self = this;
-    self.el.empty();
-    var data = this.getData();
-    Object.keys(data).sort(function(a, b){
-        return self.getPosition(a) - self.getPosition(b);
-    }).forEach(function(key){
-        var value = data[key];
-        var inputClass = self.getInputClass(key);
-        var input = new inputClass(key, value, self.getInputOptions(key));
-        self.inputs[key] = input;
-        self.el.append(input.render());
-    });
-    self.el.append(this.renderFieldSelector());
-};
-
-SdrDevice.prototype.renderFieldSelector = function() {
-    var self = this;
-    return '' +
-        '
Add new configuration options' +
-        '' +
-    '