export function Name() { return "Corsair Virtuoso XT Headset"; } export function VendorId() { return 0x1b1c; } export function ProductId() { return 0x0A64; } export function Publisher() { return "WhirlwindFX"; } export function Documentation(){ return "troubleshooting/corsair"; } export function Size() { return [1, 1]; } export function DefaultPosition(){return [145, 85];} export function DefaultScale(){return 10.0;} /* global LightingMode:readonly forcedColor:readonly micLedMode:readonly micLedDefState:readonly micColorOn:readonly micColorOff:readonly idleTimeout:readonly idleTimeoutLength:readonly SidetoneAmount:readonly */ export function ControllableParameters() { return [ {"property":"LightingMode", "group":"lighting", "label":"Lighting Mode", "type":"combobox", "values":["Canvas", "Forced"], "default":"Canvas"}, {"property":"forcedColor", "group":"lighting", "label":"Forced Color", "min":"0", "max":"360", "type":"color", "default":"#009bde"}, {"property":"micLedMode", "group":"lighting", "label":"Microphone LED Mode", "type":"combobox", "values":["Effect", "MicState"], "default":"Effect"}, {"property":"micLedDefState", "group":"lighting", "label":"Microphone Default State", "type":"combobox", "values":["OFF", "ON"], "default":"OFF"}, {"property":"micColorOn", "group":"lighting", "label":"Microphone Color ON", "min":"0", "max":"360", "type":"color", "default":"#33cc33"}, {"property":"micColorOff", "group":"lighting", "label":"Microphone Color OFF", "min":"0", "max":"360", "type":"color", "default":"#cc0000"}, {"property":"critBatLighting", "group":"lighting", "label":"Lighting on Critical Battery", "type":"combobox", "values":["OFF", "ON"], "default":"OFF"}, {"property":"idleTimeout", "group":"", "label":"Enable Device Sleep", "type":"boolean", "default":"true"}, {"property":"idleTimeoutLength", "group":"", "label":"Device Sleep Timeout (Minutes)", "type":"combobox", "values":["1","2","3","4","5","10","15","20","25","30"], "default":"10"}, {"property":"SidetoneAmount", "group":"", "label":"Sidetone", "step":"1", "type":"number", "min":"0", "max":"100", "default":"0"}, ]; } let headsetMode; let micState; let micDefState; let device_status; let previousBatteryLevel = -1; let batteryPercentage = -1; const vLedNames = [ "Logo", "Mic" ]; const vLedPositions = [ [0, 0], [0, 1] ]; export function LedNames() { return vLedNames; } export function LedPositions() { return vLedPositions; } export function Initialize() { micState = micLedDefState == "ON" ? 1 : 0; headsetMode = (ProductId() == 0x0a6b || ProductId() == 0x0a64) ? 0x09 : 0x08; device.set_endpoint(3, 0x0001, 0xFF42); device.addFeature("battery"); SetSoftwareMode(); setSidetone(); SetDefaultMicStatus(); setIdleTimeout(idleTimeoutLength); } export function Render() { readDevice(); // Micstatus why seperate stream?! sendColor(); } function SetDefaultMicStatus() { device.pause(1000); // Revert State?! micDefState = micLedDefState == "ON" ? 0 : 1; device.set_endpoint(3, 0x0001, 0xFF42); device.write([0x02, headsetMode, 0x01, 0x8e, 0x00, micDefState], 128); //Mic Status device.write([0x02, headsetMode, 0x01, 0x46, 0x00, micDefState], 128); //commit } function setSidetone() { device.set_endpoint(3, 0x0001, 0xFF42); const packet = []; // Header packet[0] = 0x02; packet[1] = headsetMode; packet[2] = 0x01; packet[3] = 0x47; let sidetoneValue = Math.round((SidetoneAmount / 100) * 1000); // In zwei Bytes aufteilen packet[0x05] = sidetoneValue & 0xFF; packet[0x06] = (sidetoneValue >> 8) & 0xFF; device.write(packet, 64); } export function onidleTimeoutChanged() { if (idleTimeout){ device.set_endpoint(3, 0x0001, 0xFF42); device.write([0x02, headsetMode, 0x01, 0x0d, 0x01], 128); device.pause(1); setIdleTimeout(idleTimeoutLength) }else{ device.set_endpoint(3, 0x0001, 0xFF42); device.write([0x02, headsetMode, 0x01, 0x0d], 128); device.log("Set IdleTimeout to: disabled"); } } function sendColor(shutdown = false) { device.set_endpoint(3, 0x0001, 0xFF42); let mxPxColor; const packet = []; packet[0] = 0x02; packet[1] = headsetMode; packet[2] = 0x06; packet[4] = 0x09; if(shutdown) { packet[2] = 0x01; packet[3] = 0x02; packet[4] = 0x00; device.write(packet, 64); } else { for(let iIdx = 0; iIdx < vLedPositions.length; iIdx++) { if ( batteryPercentage >= '0' && batteryPercentage <= "15" && critBatLighting == "OFF") { mxPxColor = hexToRgb("#000000"); } else if (iIdx == 1 && micLedMode == "MicState") { mxPxColor = hexToRgb(micState == 0 ? micColorOff : micColorOn); } else { const iPxX = vLedPositions[iIdx][0]; const iPxY = vLedPositions[iIdx][1]; if (LightingMode === "Forced") { mxPxColor = hexToRgb(forcedColor); } else { mxPxColor = device.color(0,0); } } packet[(iIdx*2)+8] = mxPxColor[0]; packet[(iIdx*2)+11] = mxPxColor[1]; packet[(iIdx*2)+14] = mxPxColor[2]; } device.write(packet, 64); } } export function onSidetoneAmountChanged() { setSidetone(); } function SetSoftwareMode() { device.set_endpoint(3, 0x0001, 0xFF42); device.write([0x02, headsetMode, 0x02, 0x14, 0x00, 0x00], 64); // ? device.write([0x02, headsetMode, 0x01, 0x03, 0x00, 0x02], 64); // Enable Software Mode device.write([0x02, headsetMode, 0x0D, 0x00, 0x01], 64); //Open lighting endpoint device.pause(100); device.write([0x02, headsetMode, 0x01, 0x02, 0x00, 0xe8, 0x03], 64); //Hardware Brightness 100% } function readDevice() { device.set_endpoint(3, 0x0002, 0xFF42); const packet = device.read([0x00], 16, 15); if(packet[0] == 0x03 && packet[1] == 0x01 && packet[2] == 0x01 && packet[3] == 0x10 && packet[4] == 0x00 && packet[5] == 0x02) { SetSoftwareMode(); } if(packet[0] == 0x03 && packet[2] == 0x01 && packet[3] == 0x46 && packet[4] == 0x00 && packet[5] == 0x00) { micState = 1; console.log("Microphone: ON") } if(packet[0] == 0x03 && packet[2] == 0x01 && packet[3] == 0x46 && packet[4] == 0x00 && packet[5] == 0x01) { micState = 0; console.log("Microphone: OFF") } if(packet[0] == 0x03 && packet[2] == 0x01 && packet[3] == 0x10 && packet[4] == 0x00 && packet[5] == 0x01) { device_status = "charging"; battery.setBatteryState(2) console.log("Headset: " + device_status) } if(packet[0] == 0x03 && packet[2] == 0x01 && packet[3] == 0x10 && packet[4] == 0x00 && packet[5] == 0x02) { device_status = "draining"; battery.setBatteryState(1) console.log("Headset: " + device_status) } if (packet[0] == 0x03 && packet[1] == 0x01 && packet[2] == 0x01 && packet[3] == 0x0f && packet[4] == 0x00) { let batteryValue = (packet[5] & 0xFF) | ((packet[6] & 0xFF) << 8); let maxBatteryValue = 1023; // 0xff03 in Dezimal batteryPercentage = Math.round((batteryValue / maxBatteryValue) * 100); console.log("Battery Level: " + batteryPercentage + "%"); console.log("Battery Level hex: " + packet[5].toString(16) + " " + packet[6].toString(16)); battery.setBatteryLevel(batteryPercentage); if (typeof previousBatteryLevel !== 'undefined') { if (batteryPercentage < previousBatteryLevel) { battery.setBatteryState(1); // Batterieentladung } else if (batteryPercentage > previousBatteryLevel) { battery.setBatteryState(2); // Batterieaufladung } } previousBatteryLevel = batteryPercentage; } } export function onidleTimeoutLengthChanged() { if (idleTimeout){ console.log ("Set Idle Timeout to: " + idleTimeoutLength + "Minutes") setIdleTimeout(idleTimeoutLength) } } function setIdleTimeout(idleTimeoutLength ) { applyPreConfig() applyIdleTimeout(idleTimeoutLength) } function applyIdleTimeout(idleTimeoutLength) { device.set_endpoint(3, 0x0001, 0xFF42); const packet = []; packet[0] = 0x02; packet[1] = headsetMode; packet[2] = 0x01; packet[3] = 0x0e; const timeoutValue = idleTimeoutLength * 60000; const hexValue = timeoutValue.toString(16).padStart(6, '0'); const littleEndianHex = hexValue.match(/../g).reverse(); packet[5] = parseInt(littleEndianHex[0], 16); packet[6] = parseInt(littleEndianHex[1], 16); packet[7] = parseInt(littleEndianHex[2], 16); console.log ("Idle Timeout: " + idleTimeoutLength + "Min") device.write(packet, 128); } function applyPreConfig() { device.set_endpoint(3, 0x0001, 0xFF42); device.write([0x02, headsetMode, 0x01, 0x0d, 0x00, 0x01], 128); device.pause(1); } function hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); const colors = []; colors[0] = parseInt(result[1], 16); colors[1] = parseInt(result[2], 16); colors[2] = parseInt(result[3], 16); return colors; } export function ImageUrl() { return "https://assets.signalrgb.com/devices/default/audio/headset-render.png"; } export function Shutdown() { sendColor(true); device.write([0x02, headsetMode, 0x01, 0x03, 0x00, 0x01], 64); } export function Validate(endpoint) { return endpoint.interface === 3 && (endpoint.usage === 0x0001 || endpoint.usage === 0x0002 ) && endpoint.usage_page === 0xFF42 ; }