diff --git a/Corsair_Virtuoso_XT_Headset.js b/Corsair_Virtuoso_XT_Headset.js index ae0112d..480f5a6 100644 --- a/Corsair_Virtuoso_XT_Headset.js +++ b/Corsair_Virtuoso_XT_Headset.js @@ -1,376 +1,556 @@ -export function Name() { return "Corsair Virtuoso Device"; } -export function VendorId() { return 0x1B1C; } -export function ProductId() { return Object.keys(CORSAIRdeviceLibrary.PIDLibrary); } -export function Publisher() { return "WhirlwindFx"; } -export function Documentation(){ return "troubleshooting/corsair"; } -export function Size() { return [1, 1]; } -/* global -shutdownColor:readonly -LightingMode:readonly -forcedColor:readonly -micLedMode:readonly -micMuteColor:readonly -idleTimeout:readonly -SidetoneAmount:readonly -*/ -export function ControllableParameters() { - return [ - {"property":"shutdownColor", "group":"lighting", "label":"Shutdown Color", "min":"0", "max":"360", "type":"color", "default":"#009bde"}, - {"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":["Canvas", "MuteState"], "default":"Canvas"}, - {"property":"micMuteColor", "group":"lighting", "label":"Microphone Mute Color", "min":"0", "max":"360", "type":"color", "default":"#ff0000"}, - {"property":"SidetoneAmount", "group":"", "label":"Sidetone", "step":"1", "type":"number", "min":"0", "max":"100", "default":"0"}, - ]; -} - -export function Initialize() { - CORSAIR.Initialize(); -} - -export function Render() { - CORSAIR.sendColors(); - - if(CORSAIR.getWirelessSupport()){ - CORSAIR.fetchBattery(); - } -} - -export function Shutdown(SystemSuspending) { - - if(SystemSuspending){ - // Go Dark on System Sleep/Shutdown - CORSAIR.sendColors("#000000"); - }else{ - const headsetMode = CORSAIR.getWirelessSupport() === true ? 0x09 : 0x08; - device.write([0x02, headsetMode, 0x01, 0x03, 0x00, 0x01], 64); // Hardware mode - } -} - -export function onSidetoneAmountChanged() { - CORSAIR.setSidetone(); -} - -export function onidleTimeoutChanged() { - CORSAIR.setIdleTimeout(); -} - -export class CORSAIR_Device_Protocol { - constructor() { - this.Config = { - DeviceProductID: 0x0000, - DeviceName: "Corsair Virtuoso Device", - DeviceEndpoint: { "interface": 0, "usage": 0x0000, "usage_page": 0x0000, "collection": 0x0000 }, - LedNames: [], - LedPositions: [], - Leds: [], - Wireless: false, // True equals to 0x09 and false to 0x08, - pollingInterval: 500, - lastMicStatePolling: 0, - lastMicState: 0, - lastBatteryPolling: 0, - pollingBatteryInterval: 300000, // 5 Minutes - }; - - this.chargingStates = Object.freeze({ - 1 : 2, - 2 : 1, - 3 : 4 - }); - } - - getDeviceProperties(deviceID) { return CORSAIRdeviceLibrary.LEDLibrary[deviceID];}; - - getDeviceProductId() { return this.Config.DeviceProductID; } - setDeviceProductId(productID) { this.Config.DeviceProductID = productID; } - - getDeviceName() { return this.Config.DeviceName; } - setDeviceName(deviceName) { this.Config.DeviceName = deviceName; } - - getDeviceEndpoint() { return this.Config.DeviceEndpoint; } - setDeviceEndpoint(deviceEndpoint) { this.Config.DeviceEndpoint = deviceEndpoint; } - - getLedNames() { return this.Config.LedNames; } - setLedNames(ledNames) { this.Config.LedNames = ledNames; } - - getLedPositions() { return this.Config.LedPositions; } - setLedPositions(ledPositions) { this.Config.LedPositions = ledPositions; } - - getLeds() { return this.Config.Leds; } - setLeds(leds) { this.Config.Leds = leds; } - - getDeviceImage() { return this.Config.image; } - setDeviceImage(image) { this.Config.image = image; } - - getWirelessSupport() { return this.Config.Wireless; } - setWirelessSupport(wireless) { this.Config.Wireless = wireless; } - - Initialize() { - //Initializing vars - this.setDeviceProductId(device.productId()); - - const DeviceProperties = this.getDeviceProperties(this.getDeviceProductId()); - this.setDeviceName(DeviceProperties.name); - this.setDeviceEndpoint(DeviceProperties.endpoint); - this.setLedNames(DeviceProperties.LedNames); - this.setLedPositions(DeviceProperties.LedPositions); - this.setLeds(DeviceProperties.Leds); - this.setDeviceImage(DeviceProperties.image); - - if(DeviceProperties.wireless){ - this.setWirelessSupport(DeviceProperties.wireless); - device.addFeature("battery"); - device.addProperty({"property":"idleTimeout", "group":"", "label":"Device Sleep Timeout (Minutes)", "type":"combobox", "values":["Off", "1", "2", "3", "4", "5", "10", "15", "20", "25", "30"], "default":"10"}); - } - - device.log("Device model found: " + this.getDeviceName()); - device.setName("Corsair " + this.getDeviceName()); - device.setSize(DeviceProperties.size); - device.setControllableLeds(this.getLedNames(), this.getLedPositions()); - device.setImageFromUrl(this.getDeviceImage()); - device.set_endpoint( - DeviceProperties.endpoint[`interface`], - DeviceProperties.endpoint[`usage`], - DeviceProperties.endpoint[`usage_page`], - DeviceProperties.endpoint[`collection`] - ); - - this.modernDirectLightingMode(); - this.fetchBattery(); - } - - modernDirectLightingMode() { - const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; - const endpoint = this.getDeviceEndpoint(); - device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); - - console.log("Setting Software Mode!"); - 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(1000); - - const HWBrightnessPacket = [0x02, headsetMode, 0x02, 0x02, 0x00]; - - device.clearReadBuffer(); - device.write(HWBrightnessPacket, 64); - device.pause(100); - - const HWBrightness = device.read(HWBrightnessPacket, 64); - - if (HWBrightness[4] !== 0xe8 || HWBrightness[5] !== 0x03) { - device.write([0x02, headsetMode, 0x01, 0x02, 0x00, 0xe8, 0x03], 64); //Hardware Brightness 100% - } - } - - sendColors(overrideColor) { - - const deviceLedPositions = this.getLedPositions(); - const deviceLeds = this.getLeds(); - const RGBData = []; - - for (let iIdx = 0; iIdx < deviceLeds.length; iIdx++) { - const iPxX = deviceLedPositions[iIdx][0]; - const iPxY = deviceLedPositions[iIdx][1]; - let color; - - if(deviceLeds[iIdx] === 2 && micLedMode === "MuteState"){ - const micState = this.fetchMicStatus(); - - if (micState === 1) { - color = hexToRgb(micMuteColor); - } else { - color = device.color(iPxX, iPxY); - } - }else if (overrideColor){ - color = hexToRgb(overrideColor); - }else if (LightingMode === "Forced") { - color = hexToRgb(forcedColor); - }else { - color = device.color(iPxX, iPxY); - } - - RGBData[(deviceLeds[iIdx])] = color[0]; - RGBData[(deviceLeds[iIdx])+3] = color[1]; - RGBData[(deviceLeds[iIdx])+6] = color[2]; - } - - this.writeRGB(RGBData); - } - - writeRGB(RGBData) { - const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; - const endpoint = this.getDeviceEndpoint(); - device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); - - device.write([0x02, headsetMode, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00].concat(RGBData), 64); - } - - fetchMicStatus(){ - - if(Date.now() - this.Config.lastMicStatePolling < this.Config.pollingInterval) { - return this.Config.lastMicState; - } - - const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; - const endpoint = this.getDeviceEndpoint(); - device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); - - const micStatusPacket = [0x02, headsetMode, 0x02, 0x46, 0x00]; - - device.pause(100); - device.clearReadBuffer(); - device.write(micStatusPacket, 64); - device.pause(50); - - const micStatus = device.read(micStatusPacket, 64); // Index 5 returns 0 to unmutted/enable and 1 to muted/disabled - this.Config.lastMicStatePolling = Date.now(); - - if (micStatus[3] === 0x46) { - console.log("Invalid read on mic status."); - - return this.Config.lastMicState; - } - - this.Config.lastMicState = micStatus[4]; - - return micStatus[4]; - - } - - setSidetone() { - const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; - const endpoint = this.getDeviceEndpoint(); - device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); - - const sidetoneValue = Math.round((SidetoneAmount / 100) * 1000); - - console.log("Setting Sidetone!"); - device.write([0x02, headsetMode, 0x01, 0x47, 0x00, sidetoneValue & 0xFF, (sidetoneValue >> 8) & 0xFF], 64); - } - - setIdleTimeout() { - const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; - const endpoint = this.getDeviceEndpoint(); - device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); - - if (idleTimeout === "Off") { - console.log ("Setting Idle Timeout to: disabled"); - device.write([0x02, headsetMode, 0x01, 0x0D], 64); - } else { - device.write([0x02, headsetMode, 0x01, 0x0D, 0x01], 64); - device.pause(1); - - device.write([0x02, headsetMode, 0x01, 0x0D, 0x00, 0x01], 64); - device.pause(1); - - const timeoutValue = idleTimeout * 60000; - const hexValue = timeoutValue.toString(16).padStart(6, '0'); - const littleEndianHex = hexValue.match(/../g).reverse(); - - const packet = []; - packet[0] = 0x02; - packet[1] = headsetMode; - packet[2] = 0x01; - packet[3] = 0x0e; - - packet[5] = parseInt(littleEndianHex[0], 16); - packet[6] = parseInt(littleEndianHex[1], 16); - packet[7] = parseInt(littleEndianHex[2], 16); - - console.log ("Setting Idle Timeout to: " + idleTimeout); - device.write(packet, 64); - } - } - - fetchBattery(){ - - if(Date.now() - this.Config.lastBatteryPolling < this.Config.pollingBatteryInterval) { - return; - } - - const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; - const endpoint = this.getDeviceEndpoint(); - device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); - - const batteryLevelPacket = [0x02, headsetMode, 0x02, 0x0F, 0x00]; - const batteryStatusPacket = [0x02, headsetMode, 0x02, 0x10, 0x00]; - - device.pause(1000); - device.clearReadBuffer(); - device.write(batteryLevelPacket, 64); - - device.pause(50); - device.clearReadBuffer(); - device.write(batteryStatusPacket, 64); - - const batteryLevelData = device.read(batteryLevelPacket, 64); - const batteryStatusData = device.read(batteryStatusPacket, 64); - - const batteryLevel = this.ReadInt32LittleEndian(batteryLevelData.slice(4, 7)); - const batteryStatus = batteryStatusData[4]; - - this.Config.lastBatteryPolling = Date.now(); - - device.log(`Battery Level is [${(batteryLevel ?? 0)/10}%]`); - device.log(`Battery Status is [${this.chargingStates[batteryStatus ?? 0]}]`); - - battery.setBatteryLevel((batteryLevel ?? 0)/ 10); - battery.setBatteryState(this.chargingStates[batteryStatus ?? 0]); - } - - ReadInt32LittleEndian(array){ - return (array[0] & 0xFF) | ((array[1] << 8) & 0xFF00) | ((array[2] << 16) & 0xFF0000) | ((array[3] << 24) & 0xFF000000); - } -} - -export class deviceLibrary { - constructor(){ - this.PIDLibrary = { - 0x0A62: "Virtuoso XT", - 0x0A64: "Virtuoso XT", // Wireless - }; - - this.LEDLibrary = { - 0x0A62: { - name: "Virtuoso XT", - size: [1, 2], - LedNames: ["Logo", "Mic"], - LedPositions: [[0, 0], [0, 1]], - Leds: [0, 2], - endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0005 }, - image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-xt.png" - }, - 0x0A64: { - name: "Virtuoso XT Wireless", - size: [1, 2], - LedNames: ["Logo", "Mic"], - LedPositions: [[0, 0], [0, 1]], - Leds: [0, 2], - wireless: true, - endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0005 }, - image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-xt.png" - }, - }; - } -} - -const CORSAIRdeviceLibrary = new deviceLibrary(); -const CORSAIR = new CORSAIR_Device_Protocol(); - -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 Validate(endpoint) { - return endpoint.interface === 3; -} - -export function ImageUrl() { - return "https://assets.signalrgb.com/devices/default/misc/usb-drive-render.png"; -} +export function Name() { return "Corsair Headset Device"; } +export function VendorId() { return 0x1B1C; } +export function ProductId() { return Object.keys(CORSAIRdeviceLibrary.PIDLibrary); } +export function Publisher() { return "WhirlwindFx"; } +export function Documentation(){ return "troubleshooting/corsair"; } +export function Size() { return [1, 1]; } +/* global +LightingMode:readonly +forcedColor:readonly +micLedMode:readonly +micMuteColor:readonly +idleTimeout: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":["Canvas", "MuteState"], "default":"Canvas"}, + {"property":"micMuteColor", "group":"lighting", "label":"Microphone Mute Color", "min":"0", "max":"360", "type":"color", "default":"#ff0000"}, + {"property":"SidetoneAmount", "group":"", "label":"Sidetone", "step":"1", "type":"number", "min":"0", "max":"100", "default":"0"}, + ]; +} + +export function Initialize() { + CORSAIR.Initialize(); +} + +export function Render() { + CORSAIR.sendColors(); + + if(CORSAIR.getWirelessSupport()){ + CORSAIR.fetchBattery(); + } +} + +export function Shutdown(SystemSuspending) { + + if(SystemSuspending){ + // Go Dark on System Sleep/Shutdown + CORSAIR.sendColors("#000000"); + }else{ + const headsetMode = CORSAIR.getWirelessSupport() === true ? 0x09 : 0x08; + device.write([0x02, headsetMode, 0x01, 0x03, 0x00, 0x01], 64); // Hardware mode + } +} + +export function onSidetoneAmountChanged() { + CORSAIR.setSidetone(); +} + +export function onidleTimeoutChanged() { + CORSAIR.setIdleTimeout(); +} + +export class CORSAIR_Device_Protocol { + constructor() { + this.Config = { + DeviceProductID: 0x0000, + DeviceName: "Corsair Headset Device", + DeviceEndpoint: { "interface": 0, "usage": 0x0000, "usage_page": 0x0000, "collection": 0x0000 }, + LedNames: [], + LedPositions: [], + Leds: [], + Wireless: false, // True equals to 0x09 and false to 0x08, + pollingInterval: 500, + lastMicStatePolling: 0, + lastMicState: 0, + lastBatteryPolling: 0, + pollingBatteryInterval: 300000, // 5 Minutes + }; + + this.chargingStates = Object.freeze({ + 1 : 2, + 2 : 1, + 3 : 4 + }); + } + + getDeviceProperties(deviceID) { return CORSAIRdeviceLibrary.LEDLibrary[deviceID];}; + + getDeviceProductId() { return this.Config.DeviceProductID; } + setDeviceProductId(productID) { this.Config.DeviceProductID = productID; } + + getDeviceName() { return this.Config.DeviceName; } + setDeviceName(deviceName) { this.Config.DeviceName = deviceName; } + + getDeviceEndpoint() { return this.Config.DeviceEndpoint; } + setDeviceEndpoint(deviceEndpoint) { this.Config.DeviceEndpoint = deviceEndpoint; } + + getLedNames() { return this.Config.LedNames; } + setLedNames(ledNames) { this.Config.LedNames = ledNames; } + + getLedPositions() { return this.Config.LedPositions; } + setLedPositions(ledPositions) { this.Config.LedPositions = ledPositions; } + + getLeds() { return this.Config.Leds; } + setLeds(leds) { this.Config.Leds = leds; } + + getDeviceImage() { return this.Config.image; } + setDeviceImage(image) { this.Config.image = image; } + + getWirelessSupport() { return this.Config.Wireless; } + setWirelessSupport(wireless) { this.Config.Wireless = wireless; } + + Initialize() { + //Initializing vars + this.setDeviceProductId(device.productId()); + + const DeviceProperties = this.getDeviceProperties(this.getDeviceProductId()); + this.setDeviceName(DeviceProperties.name); + this.setDeviceEndpoint(DeviceProperties.endpoint); + this.setLedNames(DeviceProperties.LedNames); + this.setLedPositions(DeviceProperties.LedPositions); + this.setLeds(DeviceProperties.Leds); + this.setDeviceImage(DeviceProperties.image); + + if(DeviceProperties.wireless){ + this.setWirelessSupport(DeviceProperties.wireless); + device.addFeature("battery"); + device.addProperty({"property":"idleTimeout", "group":"", "label":"Device Sleep Timeout (Minutes)", "type":"combobox", "values":["Off", "1", "2", "3", "4", "5", "10", "15", "20", "25", "30"], "default":"10"}); + } + + device.log("Device model found: " + this.getDeviceName()); + device.setName("Corsair " + this.getDeviceName()); + device.setSize(DeviceProperties.size); + device.setControllableLeds(this.getLedNames(), this.getLedPositions()); + device.setImageFromUrl(this.getDeviceImage()); + device.set_endpoint( + DeviceProperties.endpoint[`interface`], + DeviceProperties.endpoint[`usage`], + DeviceProperties.endpoint[`usage_page`], + DeviceProperties.endpoint[`collection`] + ); + + this.modernDirectLightingMode(); + } + + modernDirectLightingMode() { + const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; + const endpoint = this.getDeviceEndpoint(); + device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); + + console.log("Setting Software Mode!"); + 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(1000); + + const HWBrightnessPacket = [0x02, headsetMode, 0x02, 0x02, 0x00]; + + device.clearReadBuffer(); + device.write(HWBrightnessPacket, 64); + device.pause(100); + + const HWBrightness = device.read(HWBrightnessPacket, 64); + + if (HWBrightness[4] !== 0xe8 || HWBrightness[5] !== 0x03) { + device.write([0x02, headsetMode, 0x01, 0x02, 0x00, 0xe8, 0x03], 64); //Hardware Brightness 100% + } + } + + sendColors(overrideColor) { + + const deviceLedPositions = this.getLedPositions(); + const deviceLedNames = this.getLedNames(); + const deviceLeds = this.getLeds(); + const RGBData = []; + + for (let iIdx = 0; iIdx < deviceLeds.length; iIdx++) { + const iPxX = deviceLedPositions[iIdx][0]; + const iPxY = deviceLedPositions[iIdx][1]; + let color; + + if(deviceLedNames[iIdx] === "Mic" && micLedMode === "MuteState"){ + const micState = this.fetchMicStatus(); + + if (micState === 1) { + color = hexToRgb(micMuteColor); + } else { + color = device.color(iPxX, iPxY); + } + }else if (overrideColor){ + color = hexToRgb(overrideColor); + }else if (LightingMode === "Forced") { + color = hexToRgb(forcedColor); + }else { + color = device.color(iPxX, iPxY); + } + + RGBData[(deviceLeds[iIdx])] = color[0]; + RGBData[(deviceLeds[iIdx])+3] = color[1]; + RGBData[(deviceLeds[iIdx])+6] = color[2]; + } + + this.writeRGB(RGBData); + } + + writeRGB(RGBData) { + const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; + const endpoint = this.getDeviceEndpoint(); + device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); + + device.write([0x02, headsetMode, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00].concat(RGBData), 64); + } + + fetchMicStatus(){ + + if(Date.now() - this.Config.lastMicStatePolling < this.Config.pollingInterval) { + return this.Config.lastMicState; + } + + const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; + const micreadMode = this.getDeviceName().includes("HS80") === true ? 0xA6 : 0x46; + const endpoint = this.getDeviceEndpoint(); + device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); + + const micStatusPacket = [0x02, headsetMode, 0x02, micreadMode, 0x00]; + + device.pause(100); + device.clearReadBuffer(); + device.write(micStatusPacket, 64); + device.pause(50); + + const micStatus = device.read(micStatusPacket, 64); // Index 5 returns 0 to unmutted/enable and 1 to muted/disabled + this.Config.lastMicStatePolling = Date.now(); + + if (micStatus[3] === micreadMode) { + console.log("Invalid read on mic status."); + + return this.Config.lastMicState; + } + + this.Config.lastMicState = micStatus[4]; + + return micStatus[4]; + + } + + setSidetone() { + const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; + const endpoint = this.getDeviceEndpoint(); + device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); + + const sidetoneValue = Math.round((SidetoneAmount / 100) * 1000); + + console.log("Setting Sidetone!"); + device.write([0x02, headsetMode, 0x01, 0x47, 0x00, sidetoneValue & 0xFF, (sidetoneValue >> 8) & 0xFF], 64); + } + + setIdleTimeout() { + const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; + const endpoint = this.getDeviceEndpoint(); + device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); + + if (idleTimeout === "Off") { + console.log ("Setting Idle Timeout to: disabled"); + device.write([0x02, headsetMode, 0x01, 0x0D], 64); + } else { + device.write([0x02, headsetMode, 0x01, 0x0D, 0x01], 64); + device.pause(1); + + device.write([0x02, headsetMode, 0x01, 0x0D, 0x00, 0x01], 64); + device.pause(1); + + const timeoutValue = idleTimeout * 60000; + const hexValue = timeoutValue.toString(16).padStart(6, '0'); + const littleEndianHex = hexValue.match(/../g).reverse(); + + const packet = []; + packet[0] = 0x02; + packet[1] = headsetMode; + packet[2] = 0x01; + packet[3] = 0x0e; + + packet[5] = parseInt(littleEndianHex[0], 16); + packet[6] = parseInt(littleEndianHex[1], 16); + packet[7] = parseInt(littleEndianHex[2], 16); + + console.log ("Setting Idle Timeout to: " + idleTimeout); + device.write(packet, 64); + } + } + + fetchBattery(){ + + if(Date.now() - this.Config.lastBatteryPolling < this.Config.pollingBatteryInterval) { + return; + } + + const headsetMode = this.getWirelessSupport() === true ? 0x09 : 0x08; + const endpoint = this.getDeviceEndpoint(); + device.set_endpoint(endpoint[`interface`], endpoint[`usage`], endpoint[`usage_page`]); + + const batteryLevelPacket = [0x02, headsetMode, 0x02, 0x0F, 0x00]; + const batteryStatusPacket = [0x02, headsetMode, 0x02, 0x10, 0x00]; + + device.pause(1000); + device.clearReadBuffer(); + device.write(batteryLevelPacket, 64); + + device.pause(50); + device.clearReadBuffer(); + device.write(batteryStatusPacket, 64); + + const batteryLevelData = device.read(batteryLevelPacket, 64); + const batteryStatusData = device.read(batteryStatusPacket, 64); + + const batteryLevel = this.ReadInt32LittleEndian(batteryLevelData.slice(4, 7)); + const batteryStatus = batteryStatusData[4]; + + this.Config.lastBatteryPolling = Date.now(); + + device.log(`Battery Level is [${(batteryLevel ?? 0)/10}%]`); + device.log(`Battery Status is [${this.chargingStates[batteryStatus ?? 0]}]`); + + battery.setBatteryLevel((batteryLevel ?? 0)/ 10); + battery.setBatteryState(this.chargingStates[batteryStatus ?? 0]); + } + + ReadInt32LittleEndian(array){ + return (array[0] & 0xFF) | ((array[1] << 8) & 0xFF00) | ((array[2] << 16) & 0xFF0000) | ((array[3] << 24) & 0xFF000000); + } +} + +export class deviceLibrary { + constructor(){ + this.PIDLibrary = { + 0x0A43: "Virtuoso", + 0x0A42: "Virtuoso", + 0x0A41: "Virtuoso", + 0x0A44: "Virtuoso", + 0x0A40: "Virtuoso", + 0x0A4B: "Virtuoso", + 0x0A4C: "Virtuoso", + 0x0A5C: "Virtuoso", + 0x0A3D: "Virtuoso SE", + 0x0A3E: "Virtuoso SE", + 0x0A34: "Virtuoso SE", + 0x0A62: "Virtuoso XT", + 0x0A64: "Virtuoso XT", // Wireless + 0x0A69: "HS80", + //0x0A6A: "HS80", // PID for wired WHILE wireless dongle plugged in, doesnt control the headset + 0x0A6B: "HS80", + 0x0A71: "HS80", //White + 0x0A73: "HS80", //White + }; + + this.LEDLibrary = { + + // Virtuoso Standard + 0x0A43: { + name: "Virtuoso", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-standard.png" + }, + 0x0A42: { + name: "Virtuoso Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-standard.png" + }, + 0x0A41: { + name: "Virtuoso Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-standard.png" + }, + 0x0A44: { + name: "Virtuoso Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-standard.png" + }, + 0x0A40: { + name: "Virtuoso Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-standard.png" + }, + 0x0A4B: { + name: "Virtuoso Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-standard.png" + }, + 0x0A4C: { + name: "Virtuoso Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-standard.png" + }, + 0x0A5C: { + name: "Virtuoso Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-standard.png" + }, + + // Virtuoso SE + 0x0A3D: { + name: "Virtuoso SE", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-se.png" + }, + 0x0A3E: { + name: "Virtuoso SE Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-se.png" + }, + 0x0A346: { // NEED TO CONFIRM + name: "Virtuoso SE Wireless", + size: [3, 3], + LedNames: ["Left Cans", "Right Can", "Mic"], + LedPositions: [[0, 2], [2, 2], [1, 0]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 4, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0005 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-se.png" + }, + + // Virtuoso XT + 0x0A62: { + name: "Virtuoso XT", + size: [3, 3], + LedNames: ["Logo", "Power", "Mic"], + LedPositions: [[1, 0], [0, 2], [2, 2]], + Leds: [0, 1, 2], + endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0005 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-xt.png" + }, + 0x0A64: { + name: "Virtuoso XT Wireless", + size: [3, 3], + LedNames: ["Logo", "Power", "Mic"], + LedPositions: [[1, 0], [0, 2], [2, 2]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0005 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/virtuoso-xt.png" + }, + + // HS80 + /* + 0x0A6A: { // PID for wired WHILE wireless dongle plugged in + name: "HS80 RGB", + size: [3, 3], + LedNames: ["Logo", "Power", "Mic"], + LedPositions: [[1, 0], [0, 2], [2, 2]], + Leds: [0, 1, 2], + endpoint : { "interface": 0, "usage": 0x0001, "usage_page": 0xFF58, "collection": 0x0001 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/hs80.png" + }, + */ + 0x0A69: { + name: "HS80 RGB", + size: [3, 3], + LedNames: ["Logo", "Power", "Mic"], + LedPositions: [[1, 0], [0, 2], [2, 2]], + Leds: [0, 1, 2], + endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/hs80.png" + }, + 0x0A6B: { + name: "HS80 RGB Wireless", + size: [3, 3], + LedNames: ["Logo", "Power", "Mic"], + LedPositions: [[1, 0], [0, 2], [2, 2]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/hs80.png" + }, + 0x0A71: { + name: "HS80 RGB White", + size: [3, 3], + LedNames: ["Logo", "Power", "Mic"], + LedPositions: [[1, 0], [0, 2], [2, 2]], + Leds: [0, 1, 2], + endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0004 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/hs80.png" + }, + 0x0A73: { //White + name: "HS80 RGB White Wireless", + size: [3, 3], + LedNames: ["Logo", "Power", "Mic"], + LedPositions: [[1, 0], [0, 2], [2, 2]], + Leds: [0, 1, 2], + wireless: true, + endpoint : { "interface": 3, "usage": 0x0001, "usage_page": 0xFF42, "collection": 0x0005 }, + image: "https://assets.signalrgb.com/devices/brands/corsair/audio/hs80.png" + }, + }; + } +} + +const CORSAIRdeviceLibrary = new deviceLibrary(); +const CORSAIR = new CORSAIR_Device_Protocol(); + +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 Validate(endpoint) { + return endpoint.interface === 0 || endpoint.interface === 3 || endpoint.interface === 4; +} + +export function ImageUrl() { + return "https://assets.signalrgb.com/devices/default/misc/usb-drive-render.png"; +}