diff --git a/Software/WebConfig/css/style.css b/Software/WebConfig/css/style.css
new file mode 100644
index 0000000..22166a0
--- /dev/null
+++ b/Software/WebConfig/css/style.css
@@ -0,0 +1,28 @@
+.hidden {
+ display: none;
+}
+
+.disabled {
+ opacity: 0.4;
+}
+
+.configBox {
+ background-image: url("../img/topdown-small.png");
+ width: 800px;
+ height: 571px;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.connect {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 285px;
+ display:flex;
+ justify-content:center;
+}
+
+.connect > button {
+ font-size: 40pt;
+}
\ No newline at end of file
diff --git a/Software/WebConfig/img/topdown-small.png b/Software/WebConfig/img/topdown-small.png
new file mode 100644
index 0000000..6e2623e
Binary files /dev/null and b/Software/WebConfig/img/topdown-small.png differ
diff --git a/Software/WebConfig/img/topdown.png b/Software/WebConfig/img/topdown.png
new file mode 100644
index 0000000..6a496d6
Binary files /dev/null and b/Software/WebConfig/img/topdown.png differ
diff --git a/Software/WebConfig/index.html b/Software/WebConfig/index.html
new file mode 100644
index 0000000..c40b243
--- /dev/null
+++ b/Software/WebConfig/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+ Pocket Voltex - Config
+
+
+
+
+
+
+
+ This configurator requires Javascript, Windows 8+ (or OSX/Linux) and a WebUSB capable browser (Chrome 55+). If you lack these, give the exe config app a go.
+
+
+
+
+ Connect
+
+
+
+
diff --git a/Software/WebConfig/js/config.js b/Software/WebConfig/js/config.js
new file mode 100644
index 0000000..1403d86
--- /dev/null
+++ b/Software/WebConfig/js/config.js
@@ -0,0 +1,406 @@
+(function(window, document) {
+"use strict";
+
+var vid = 0x16D0;
+var pid = 0x0A6D;
+var UsbWrapper = window.UsbWrapper;
+
+var device;
+
+var SWITCH_COUNT = 8;
+var CONFIG_OUT_EPADDR = 1;
+var CONFIG_IN_EPADDR = 2;
+var packetSize = 64;
+
+var commands = {
+ GETCONFIG : 1,
+ SETCONFIG : 2,
+ VERSION : 3,
+ RESET : 42
+};
+
+var defaultConfig = {
+ switches : [],
+ ledsOn : true,
+ //macroClick : KP_PLUS,
+ macroPin : [1,2,3,4]
+}
+
+var visibleLog = function(html) {
+ console.log(html);
+ document.getElementById('logview').innerHTML += html + ' ';
+}
+
+var ConfigValues = function(view) {
+ // skip the returned command byte
+ var offset = 1;
+
+ this.switches = this.getKeys(view, offset, SWITCH_COUNT);
+ offset += SWITCH_COUNT;
+ this.btColour = this.getColour(view, offset);
+ offset += 3;
+ this.fxColour = this.getColour(view, offset);
+ offset += 3;
+ this.knobColours = [];
+ for(var i = 0; i < 2; i++) {
+ this.knobColours[i] = this.getColour(view, offset);
+ offset += 3;
+ }
+
+ this.lightsOn = !!(view.getUint8(offset++));
+ this.hidLights = !!(view.getUint8(offset++));
+ this.keyLights = !!(view.getUint8(offset++));
+ this.knobLights = !!(view.getUint8(offset++));
+ this.lightPattern = view.getUint8(offset++);
+ this.macroClick = this.getKey(view, offset++);
+ this.macroPin = this.getArray(view, offset, 4);
+ offset += 4;
+ this.joystickMode = !!(view.getUint8(offset++));
+}
+
+ConfigValues.prototype.getArray = function(view, offset, size) {
+ var ret = [];
+ for(var i = 0; i < size; i++) {
+ ret[i] = view.getUint8(offset+i);
+ }
+ return ret;
+};
+
+ConfigValues.prototype.getColour = function(view, offset) {
+ return 'rgb(' + this.getArray(view, offset, 3).join() + ')';
+};
+
+ConfigValues.prototype.getKeys = function(view, offset, size) {
+ var keys = [];
+ for(let i = 0; i < size; i++) {
+ keys[i] = this.getKey(view, offset++);
+ }
+ return keys;
+}
+
+ConfigValues.prototype.getKey = function(view, offset) {
+ var key = view.getUint8(offset);
+ return scancodes.find(code => {return code.value == key});
+}
+
+var Config = new function() {
+ this.connect = () => {
+ return UsbWrapper.connect(vid, pid)
+ .then(selectedDevice => {
+ device = selectedDevice;
+ visibleLog("Opening device...");
+ return device.open();
+ })
+ .then(() => {
+ console.log("Opened, selecting configuration...");
+ return device.selectConfiguration(1)
+ })
+ .then(() => {
+ console.log("Selected, claiming interface...");
+ return device.claimInterface(0);
+ })
+ .then(() => {
+ visibleLog("Opened!");
+ return this.readVersion();
+ })
+ .then(version => {
+ console.log("Found Pocket Voltex v" + version/10.0);
+ return this.readConfig();
+ })
+ .then(result => {console.log(result);})
+ .then( () => {
+ if(device && device.opened)
+ device.close();
+ })
+ .catch(error => {
+ //alert(error);
+ visibleLog(error);
+ if(device && device.opened)
+ device.close();
+ });
+ };
+
+ this.readVersion = () => {
+ if(!device || !device.opened)
+ return Promise.reject("Device not opened");
+
+ var data = new Uint8Array(packetSize);
+ data[0] = commands.VERSION;
+ return device.transferOut(CONFIG_OUT_EPADDR, data)
+ .then(() => device.transferIn(CONFIG_IN_EPADDR, packetSize))
+ .then(result => {
+ console.log(result);
+ console.log("Got version response of len", result.data.buffer.byteLength);
+ // version int exists at offset 1
+ return result.data.getUint16(1, true);
+ })
+ };
+
+ this.readConfig = () => {
+ if(!device || !device.opened)
+ return Promise.reject("Device not opened");
+ var data = new Uint8Array(packetSize);
+ data[0] = commands.GETCONFIG;
+ return device.transferOut(CONFIG_OUT_EPADDR, data)
+ .then(() => device.transferIn(CONFIG_IN_EPADDR, packetSize))
+ .then(result => {
+ console.log("Got config response of len", result.data.buffer.byteLength);
+ return new ConfigValues(result.data);
+ });
+ };
+
+ this.close = () => {
+ if(!device || !device.opened)
+ return Promise.reject("Device not opened");
+ visibleLog("Closing device...");
+ return device.close();
+ };
+
+ this.rebootToBootloader = () => {
+ if(!device || !device.opened)
+ return Promise.reject("Device not opened");
+ var data = new Uint8Array(packetSize);
+ data[0] = commands.RESET;
+ return device.transferOut(1, data);
+ };
+}
+
+window.Config = Config;
+
+var scancodes = [
+ {name: "A", value: 0x04},
+ {name: "B", value: 0x05},
+ {name: "C", value: 0x06},
+ {name: "D", value: 0x07},
+ {name: "E", value: 0x08},
+ {name: "F", value: 0x09},
+ {name: "G", value: 0x0A},
+ {name: "H", value: 0x0B},
+ {name: "I", value: 0x0C},
+ {name: "J", value: 0x0D},
+ {name: "K", value: 0x0E},
+ {name: "L", value: 0x0F},
+ {name: "M", value: 0x10},
+ {name: "N", value: 0x11},
+ {name: "O", value: 0x12},
+ {name: "P", value: 0x13},
+ {name: "Q", value: 0x14},
+ {name: "R", value: 0x15},
+ {name: "S", value: 0x16},
+ {name: "T", value: 0x17},
+ {name: "U", value: 0x18},
+ {name: "V", value: 0x19},
+ {name: "W", value: 0x1A},
+ {name: "X", value: 0x1B},
+ {name: "Y", value: 0x1C},
+ {name: "Z", value: 0x1D},
+ {name: "1", value: 0x1E},
+ {name: "2", value: 0x1F},
+ {name: "3", value: 0x20},
+ {name: "4", value: 0x21},
+ {name: "5", value: 0x22},
+ {name: "6", value: 0x23},
+ {name: "7", value: 0x24},
+ {name: "8", value: 0x25},
+ {name: "9", value: 0x26},
+ {name: "0", value: 0x27},
+ {name: "-_", value: 0x2D},
+ {name: "=+", value: 0x2E},
+ {name: "[{", value: 0x2F},
+ {name: "]}", value: 0x30},
+ {name: "\\|", value: 0x31},
+ {name: ";:", value: 0x33},
+ {name: "'\"", value: 0x34},
+ {name: "`~", value: 0x35},
+ {name: ",<", value: 0x36},
+ {name: ".>", value: 0x37},
+ {name: "/?", value: 0x38},
+ {name: "F1", value: 0x3A},
+ {name: "F2", value: 0x3B},
+ {name: "F3", value: 0x3C},
+ {name: "F4", value: 0x3D},
+ {name: "F5", value: 0x3E},
+ {name: "F6", value: 0x3F},
+ {name: "F7", value: 0x40},
+ {name: "F8", value: 0x41},
+ {name: "F9", value: 0x42},
+ {name: "F10", value: 0x43},
+ {name: "F11", value: 0x44},
+ {name: "F12", value: 0x45},
+ {name: "ESC", value: 0x29},
+ {name: "BACKSPC", value: 0x2A},
+ {name: "TAB", value: 0x2B},
+ {name: "SPACE", value: 0x2C},
+ {name: "ENTER", value: 0x28},
+ {name: "CAPSLOCK", value: 0x39},
+ {name: "PRTSCRN", value: 0x46},
+ {name: "SCRL LCK", value: 0x47},
+ {name: "PAUSE", value: 0x48},
+ {name: "INSERT", value: 0x49},
+ {name: "HOME", value: 0x4A},
+ {name: "PG UP", value: 0x4B},
+ {name: "DEL", value: 0x4C},
+ {name: "END", value: 0x4D},
+ {name: "PG DN", value: 0x4E},
+ {name: "RIGHT", value: 0x4F},
+ {name: "LEFT", value: 0x50},
+ {name: "DOWN", value: 0x51},
+ {name: "UP", value: 0x52},
+ {name: "NUM LOCK", value: 0x53},
+ {name: "NUM /", value: 0x54},
+ {name: "NUM *", value: 0x55},
+ {name: "NUM -", value: 0x56},
+ {name: "NUM +", value: 0x57},
+ {name: "NUM ENTER", value: 0x58},
+ {name: "NUM 1", value: 0x59},
+ {name: "NUM 2", value: 0x5A},
+ {name: "NUM 3", value: 0x5B},
+ {name: "NUM 4", value: 0x5C},
+ {name: "NUM 5", value: 0x5D},
+ {name: "NUM 6", value: 0x5E},
+ {name: "NUM 7", value: 0x5F},
+ {name: "NUM 8", value: 0x60},
+ {name: "NUM 9", value: 0x61},
+ {name: "NUM 0", value: 0x62},
+ {name: "NUM .", value: 0x63}
+];
+
+//{name: "NON US BACKSLASH AND PIPE", value: 0x64},
+//{name: "APPLICATION", value: 0x65},
+//{name: "POWER", value: 0x66},
+//{name: "KEYPAD EQUAL SIGN", value: 0x67},
+//{name: "F13", value: 0x68},
+//{name: "F14", value: 0x69},
+//{name: "F15", value: 0x6A},
+//{name: "F16", value: 0x6B},
+//{name: "F17", value: 0x6C},
+//{name: "F18", value: 0x6D},
+//{name: "F19", value: 0x6E},
+//{name: "F20", value: 0x6F},
+//{name: "F21", value: 0x70},
+//{name: "F22", value: 0x71},
+//{name: "F23", value: 0x72},
+//{name: "F24", value: 0x73},
+//{name: "EXECUTE", value: 0x74},
+//{name: "HELP", value: 0x75},
+//{name: "MENU", value: 0x76},
+//{name: "SELECT", value: 0x77},
+//{name: "STOP", value: 0x78},
+//{name: "AGAIN", value: 0x79},
+//{name: "UNDO", value: 0x7A},
+//{name: "CUT", value: 0x7B},
+//{name: "COPY", value: 0x7C},
+//{name: "PASTE", value: 0x7D},
+//{name: "FIND", value: 0x7E},
+//{name: "MUTE", value: 0x7F},
+//{name: "VOLUME UP", value: 0x80},
+//{name: "VOLUME DOWN", value: 0x81},
+//{name: "LOCKING CAPS LOCK", value: 0x82},
+//{name: "LOCKING NUM LOCK", value: 0x83},
+//{name: "LOCKING SCROLL LOCK", value: 0x84},
+//{name: "KEYPAD COMMA", value: 0x85},
+//{name: "KEYPAD EQUAL SIGN AS400", value: 0x86},
+//{name: "INTERNATIONAL1", value: 0x87},
+//{name: "INTERNATIONAL2", value: 0x88},
+//{name: "INTERNATIONAL3", value: 0x89},
+//{name: "INTERNATIONAL4", value: 0x8A},
+//{name: "INTERNATIONAL5", value: 0x8B},
+//{name: "INTERNATIONAL6", value: 0x8C},
+//{name: "INTERNATIONAL7", value: 0x8D},
+//{name: "INTERNATIONAL8", value: 0x8E},
+//{name: "INTERNATIONAL9", value: 0x8F},
+//{name: "LANG1", value: 0x90},
+//{name: "LANG2", value: 0x91},
+//{name: "LANG3", value: 0x92},
+//{name: "LANG4", value: 0x93},
+//{name: "LANG5", value: 0x94},
+//{name: "LANG6", value: 0x95},
+//{name: "LANG7", value: 0x96},
+//{name: "LANG8", value: 0x97},
+//{name: "LANG9", value: 0x98},
+//{name: "ALTERNATE ERASE", value: 0x99},
+//{name: "SYSREQ", value: 0x9A},
+//{name: "CANCEL", value: 0x9B},
+//{name: "CLEAR", value: 0x9C},
+//{name: "PRIOR", value: 0x9D},
+//{name: "RETURN", value: 0x9E},
+//{name: "SEPARATOR", value: 0x9F},
+//{name: "OUT", value: 0xA0},
+//{name: "OPER", value: 0xA1},
+//{name: "CLEAR AND AGAIN", value: 0xA2},
+//{name: "CRSEL AND PROPS", value: 0xA3},
+//{name: "EXSEL", value: 0xA4},
+//{name: "KEYPAD 00", value: 0xB0},
+//{name: "KEYPAD 000", value: 0xB1},
+//{name: "THOUSANDS SEPARATOR", value: 0xB2},
+//{name: "DECIMAL SEPARATOR", value: 0xB3},
+//{name: "CURRENCY UNIT", value: 0xB4},
+//{name: "CURRENCY SUB UNIT", value: 0xB5},
+//{name: "KEYPAD OPENING PARENTHESIS", value: 0xB6},
+//{name: "KEYPAD CLOSING PARENTHESIS", value: 0xB7},
+//{name: "KEYPAD OPENING BRACE", value: 0xB8},
+//{name: "KEYPAD CLOSING BRACE", value: 0xB9},
+//{name: "KEYPAD TAB", value: 0xBA},
+//{name: "KEYPAD BACKSPACE", value: 0xBB},
+//{name: "KEYPAD A", value: 0xBC},
+//{name: "KEYPAD B", value: 0xBD},
+//{name: "KEYPAD C", value: 0xBE},
+//{name: "KEYPAD D", value: 0xBF},
+//{name: "KEYPAD E", value: 0xC0},
+//{name: "KEYPAD F", value: 0xC1},
+//{name: "KEYPAD XOR", value: 0xC2},
+//{name: "KEYPAD CARET", value: 0xC3},
+//{name: "KEYPAD PERCENTAGE", value: 0xC4},
+//{name: "KEYPAD LESS THAN SIGN", value: 0xC5},
+//{name: "KEYPAD GREATER THAN SIGN", value: 0xC6},
+//{name: "KEYPAD AMP", value: 0xC7},
+//{name: "KEYPAD AMP AMP", value: 0xC8},
+//{name: "KEYPAD PIPE", value: 0xC9},
+//{name: "KEYPAD PIPE PIPE", value: 0xCA},
+//{name: "KEYPAD COLON", value: 0xCB},
+//{name: "KEYPAD HASHMARK", value: 0xCC},
+//{name: "KEYPAD SPACE", value: 0xCD},
+//{name: "KEYPAD AT", value: 0xCE},
+//{name: "KEYPAD EXCLAMATION SIGN", value: 0xCF},
+//{name: "KEYPAD MEMORY STORE", value: 0xD0},
+//{name: "KEYPAD MEMORY RECALL", value: 0xD1},
+//{name: "KEYPAD MEMORY CLEAR", value: 0xD2},
+//{name: "KEYPAD MEMORY ADD", value: 0xD3},
+//{name: "KEYPAD MEMORY SUBTRACT", value: 0xD4},
+//{name: "KEYPAD MEMORY MULTIPLY", value: 0xD5},
+//{name: "KEYPAD MEMORY DIVIDE", value: 0xD6},
+//{name: "KEYPAD PLUS AND MINUS", value: 0xD7},
+//{name: "KEYPAD CLEAR", value: 0xD8},
+//{name: "KEYPAD CLEAR ENTRY", value: 0xD9},
+//{name: "KEYPAD BINARY", value: 0xDA},
+//{name: "KEYPAD OCTAL", value: 0xDB},
+//{name: "KEYPAD DECIMAL", value: 0xDC},
+//{name: "KEYPAD HEXADECIMAL", value: 0xDD},
+//{name: "LEFT CONTROL", value: 0xE0},
+//{name: "LEFT SHIFT", value: 0xE1},
+//{name: "LEFT ALT", value: 0xE2},
+//{name: "LEFT GUI", value: 0xE3},
+//{name: "RIGHT CONTROL", value: 0xE4},
+//{name: "RIGHT SHIFT", value: 0xE5},
+//{name: "RIGHT ALT", value: 0xE6},
+//{name: "RIGHT GUI", value: 0xE7},
+//{name: "MEDIA PLAY", value: 0xE8},
+//{name: "MEDIA STOP", value: 0xE9},
+//{name: "MEDIA PREVIOUS TRACK", value: 0xEA},
+//{name: "MEDIA NEXT TRACK", value: 0xEB},
+//{name: "MEDIA EJECT", value: 0xEC},
+//{name: "MEDIA VOLUME UP", value: 0xED},
+//{name: "MEDIA VOLUME DOWN", value: 0xEE},
+//{name: "MEDIA MUTE", value: 0xEF},
+//{name: "MEDIA WWW", value: 0xF0},
+//{name: "MEDIA BACKWARD", value: 0xF1},
+//{name: "MEDIA FORWARD", value: 0xF2},
+//{name: "MEDIA CANCEL", value: 0xF3},
+//{name: "MEDIA SEARCH", value: 0xF4},
+//{name: "MEDIA SLEEP", value: 0xF8},
+//{name: "MEDIA LOCK", value: 0xF9},
+//{name: "MEDIA RELOAD", value: 0xFA},
+//{name: "MEDIA CALCULATOR", value: 0xFB}
+//{name: "NON_US_HASHMARK_AND_TILDE", value: 0x32},
+
+})(window, document);
\ No newline at end of file
diff --git a/Software/WebConfig/js/usbWrapper.js b/Software/WebConfig/js/usbWrapper.js
new file mode 100644
index 0000000..8726ce8
--- /dev/null
+++ b/Software/WebConfig/js/usbWrapper.js
@@ -0,0 +1,19 @@
+(function(window, document, navigator) {
+"use strict";
+
+var UsbWrapper = new function() {
+ this.hasUSB = navigator && navigator.usb;
+
+ // returns a promise
+ this.connect = (vid, pid) => {
+ return navigator.usb.requestDevice({ filters: [
+ { vendorId: vid, productId: pid }] })
+ .catch(error => {
+ console.log(error);
+ return null;
+ });
+ };
+};
+
+window.UsbWrapper = UsbWrapper;
+})(window, document, navigator);
\ No newline at end of file