2024-06-01 22:48:42 +02:00
|
|
|
export function Name() { return "Corsair Virtuoso XT Headset"; }
|
2024-06-01 22:10:56 +02:00
|
|
|
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
|
2024-06-18 20:12:39 +02:00
|
|
|
idleTimeout:readonly
|
|
|
|
idleTimeoutLength:readonly
|
|
|
|
SidetoneAmount:readonly
|
2024-06-01 22:10:56 +02:00
|
|
|
*/
|
2024-06-18 20:12:39 +02:00
|
|
|
|
2024-06-01 22:10:56 +02:00
|
|
|
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"},
|
2024-06-18 23:43:09 +02:00
|
|
|
{"property":"critBatLighting", "group":"lighting", "label":"Lighting on Critical Battery", "type":"combobox", "values":["OFF", "ON"], "default":"OFF"},
|
2024-06-18 20:12:39 +02:00
|
|
|
{"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"},
|
|
|
|
|
|
|
|
|
2024-06-01 22:10:56 +02:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
let headsetMode;
|
|
|
|
let micState;
|
2024-06-18 20:12:39 +02:00
|
|
|
let micDefState;
|
|
|
|
let device_status;
|
|
|
|
let previousBatteryLevel = -1;
|
2024-06-18 23:43:09 +02:00
|
|
|
let batteryPercentage = -1;
|
2024-06-01 22:10:56 +02:00
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
const vLedNames = [
|
|
|
|
"Logo", "Mic"
|
|
|
|
];
|
2024-06-01 22:10:56 +02:00
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
const vLedPositions = [
|
|
|
|
[0, 0], [0, 1]
|
|
|
|
];
|
2024-06-01 22:10:56 +02:00
|
|
|
|
|
|
|
export function LedNames() {
|
|
|
|
return vLedNames;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function LedPositions() {
|
|
|
|
return vLedPositions;
|
|
|
|
}
|
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
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);
|
2024-06-19 23:36:29 +02:00
|
|
|
device.write([0x02, headsetMode, 0x01, 0x8e, 0x00, micDefState], 128); //Mic Status
|
|
|
|
device.write([0x02, headsetMode, 0x01, 0x46, 0x00, micDefState], 128); //commit
|
2024-06-18 20:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2024-06-19 23:04:20 +02:00
|
|
|
device.write([0x02, headsetMode, 0x01, 0x0d, 0x01], 128);
|
2024-06-18 20:12:39 +02:00
|
|
|
device.pause(1);
|
|
|
|
setIdleTimeout(idleTimeoutLength)
|
|
|
|
}else{
|
|
|
|
device.set_endpoint(3, 0x0001, 0xFF42);
|
2024-06-19 23:04:20 +02:00
|
|
|
device.write([0x02, headsetMode, 0x01, 0x0d], 128);
|
2024-06-18 20:12:39 +02:00
|
|
|
device.log("Set IdleTimeout to: disabled");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-01 22:10:56 +02:00
|
|
|
function sendColor(shutdown = false) {
|
2024-06-18 23:43:09 +02:00
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
device.set_endpoint(3, 0x0001, 0xFF42);
|
2024-06-01 22:10:56 +02:00
|
|
|
let mxPxColor;
|
2024-06-18 20:12:39 +02:00
|
|
|
const packet = [];
|
2024-06-01 22:10:56 +02:00
|
|
|
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);
|
2024-06-18 23:43:09 +02:00
|
|
|
} else {
|
2024-06-01 22:10:56 +02:00
|
|
|
for(let iIdx = 0; iIdx < vLedPositions.length; iIdx++) {
|
2024-06-18 23:43:09 +02:00
|
|
|
if ( batteryPercentage >= '0' && batteryPercentage <= "15" && critBatLighting == "OFF") {
|
|
|
|
mxPxColor = hexToRgb("#000000");
|
|
|
|
} else if (iIdx == 1 && micLedMode == "MicState") {
|
2024-06-01 22:10:56 +02:00
|
|
|
mxPxColor = hexToRgb(micState == 0 ? micColorOff : micColorOn);
|
2024-06-18 20:12:39 +02:00
|
|
|
} else {
|
2024-06-01 22:10:56 +02:00
|
|
|
const iPxX = vLedPositions[iIdx][0];
|
|
|
|
const iPxY = vLedPositions[iIdx][1];
|
|
|
|
if (LightingMode === "Forced") {
|
|
|
|
mxPxColor = hexToRgb(forcedColor);
|
|
|
|
} else {
|
2024-06-18 20:12:39 +02:00
|
|
|
mxPxColor = device.color(0,0);
|
2024-06-01 22:10:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
packet[(iIdx*2)+8] = mxPxColor[0];
|
|
|
|
packet[(iIdx*2)+11] = mxPxColor[1];
|
|
|
|
packet[(iIdx*2)+14] = mxPxColor[2];
|
|
|
|
}
|
|
|
|
device.write(packet, 64);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
export function onSidetoneAmountChanged() {
|
|
|
|
setSidetone();
|
2024-06-01 22:10:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function SetSoftwareMode() {
|
2024-06-18 20:12:39 +02:00
|
|
|
device.set_endpoint(3, 0x0001, 0xFF42);
|
2024-06-19 22:52:51 +02:00
|
|
|
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
|
2024-06-19 23:33:22 +02:00
|
|
|
device.pause(100);
|
2024-06-19 23:30:00 +02:00
|
|
|
device.write([0x02, headsetMode, 0x01, 0x02, 0x00, 0xe8, 0x03], 64); //Hardware Brightness 100%
|
|
|
|
|
2024-06-19 22:52:51 +02:00
|
|
|
|
2024-06-01 22:10:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function readDevice() {
|
2024-06-18 20:12:39 +02:00
|
|
|
device.set_endpoint(3, 0x0002, 0xFF42);
|
|
|
|
const packet = device.read([0x00], 16, 15);
|
|
|
|
|
2024-06-01 22:10:56 +02:00
|
|
|
if(packet[0] == 0x03 && packet[1] == 0x01 && packet[2] == 0x01 && packet[3] == 0x10 && packet[4] == 0x00 && packet[5] == 0x02) {
|
|
|
|
SetSoftwareMode();
|
2024-06-18 20:12:39 +02:00
|
|
|
}
|
2024-06-01 22:10:56 +02:00
|
|
|
|
2024-06-01 22:40:33 +02:00
|
|
|
if(packet[0] == 0x03 && packet[2] == 0x01 && packet[3] == 0x46 && packet[4] == 0x00 && packet[5] == 0x00) {
|
2024-06-01 22:10:56 +02:00
|
|
|
micState = 1;
|
2024-06-18 20:12:39 +02:00
|
|
|
console.log("Microphone: ON")
|
2024-06-01 22:10:56 +02:00
|
|
|
}
|
|
|
|
|
2024-06-01 22:40:33 +02:00
|
|
|
if(packet[0] == 0x03 && packet[2] == 0x01 && packet[3] == 0x46 && packet[4] == 0x00 && packet[5] == 0x01) {
|
2024-06-01 22:10:56 +02:00
|
|
|
micState = 0;
|
2024-06-18 20:12:39 +02:00
|
|
|
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);
|
2024-06-18 23:43:09 +02:00
|
|
|
let maxBatteryValue = 1023; // 0xff03 in Dezimal
|
|
|
|
batteryPercentage = Math.round((batteryValue / maxBatteryValue) * 100);
|
2024-06-18 20:12:39 +02:00
|
|
|
console.log("Battery Level: " + batteryPercentage + "%");
|
2024-06-18 23:43:09 +02:00
|
|
|
console.log("Battery Level hex: " + packet[5].toString(16) + " " + packet[6].toString(16));
|
2024-06-18 20:12:39 +02:00
|
|
|
battery.setBatteryLevel(batteryPercentage);
|
|
|
|
if (typeof previousBatteryLevel !== 'undefined') {
|
|
|
|
if (batteryPercentage < previousBatteryLevel) {
|
|
|
|
battery.setBatteryState(1); // Batterieentladung
|
|
|
|
} else if (batteryPercentage > previousBatteryLevel) {
|
|
|
|
battery.setBatteryState(2); // Batterieaufladung
|
|
|
|
}
|
|
|
|
}
|
|
|
|
previousBatteryLevel = batteryPercentage;
|
2024-06-01 22:10:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
export function onidleTimeoutLengthChanged() {
|
|
|
|
if (idleTimeout){
|
|
|
|
console.log ("Set Idle Timeout to: " + idleTimeoutLength + "Minutes")
|
|
|
|
setIdleTimeout(idleTimeoutLength)
|
|
|
|
}
|
|
|
|
}
|
2024-06-01 22:10:56 +02:00
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
function setIdleTimeout(idleTimeoutLength ) {
|
|
|
|
applyPreConfig()
|
|
|
|
applyIdleTimeout(idleTimeoutLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
function applyIdleTimeout(idleTimeoutLength) {
|
|
|
|
device.set_endpoint(3, 0x0001, 0xFF42);
|
|
|
|
|
|
|
|
const packet = [];
|
2024-06-01 22:10:56 +02:00
|
|
|
packet[0] = 0x02;
|
|
|
|
packet[1] = headsetMode;
|
|
|
|
packet[2] = 0x01;
|
2024-06-18 20:12:39 +02:00
|
|
|
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);
|
2024-06-01 22:10:56 +02:00
|
|
|
}
|
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
function applyPreConfig() {
|
|
|
|
device.set_endpoint(3, 0x0001, 0xFF42);
|
2024-06-19 23:04:20 +02:00
|
|
|
device.write([0x02, headsetMode, 0x01, 0x0d, 0x00, 0x01], 128);
|
2024-06-18 20:12:39 +02:00
|
|
|
device.pause(1);
|
2024-06-01 22:10:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-06-18 20:12:39 +02:00
|
|
|
export function ImageUrl() {
|
|
|
|
return "https://assets.signalrgb.com/devices/default/audio/headset-render.png";
|
2024-06-01 22:10:56 +02:00
|
|
|
}
|
2024-06-18 20:12:39 +02:00
|
|
|
|
|
|
|
export function Shutdown() {
|
|
|
|
sendColor(true);
|
2024-06-19 23:33:22 +02:00
|
|
|
device.write([0x02, headsetMode, 0x01, 0x03, 0x00, 0x01], 64);
|
2024-06-18 20:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export function Validate(endpoint) {
|
|
|
|
return endpoint.interface === 3 && (endpoint.usage === 0x0001 || endpoint.usage === 0x0002 ) && endpoint.usage_page === 0xFF42 ;
|
|
|
|
}
|