export function Name() { return "HyperX Eve 1800"; } export function VendorId() { return 0x03F0; } export function ProductId() { return 0x08C2; } export function Publisher() { return "Steve Ferguson"; } export function Size() { return [21, 6]; } export function DefaultPosition() { return [10, 100]; } export function DefaultScale() { return 8.0; } export function DeviceType() { return "keyboard"; } export function ConflictingProcesses() { return ["NGenuity2.exe"]; } /* global shutdownColor:readonly LightingMode:readonly forcedColor:readonly */ export function ControllableParameters() { return [ {"property":"shutdownColor", "group":"lighting", "label":"Shutdown Color", description: "This color is applied to the device when the System, or SignalRGB is shutting down", "min":"0", "max":"360", "type":"color", "default":"#000000"}, {"property":"LightingMode", "group":"lighting", "label":"Lighting Mode", description: "Determines where the device's RGB comes from. Canvas will pull from the active Effect, while Forced will override it to a specific color", "type":"combobox", "values":["Canvas", "Forced"], "default":"Canvas"}, {"property":"forcedColor", "group":"lighting", "label":"Forced Color", description: "The color used when 'Forced' Lighting Mode is enabled", "min":"0", "max":"360", "type":"color", "default":"#009bde"}, ]; } // The Eve 1800 has 10 lighting zones, each a vertical strip of the board, // ordered left to right. X positions follow the physical key columns // (roughly one unit per key) so effects sweep across the board evenly. const vLedNames = [ "Zone 1 (Esc, F1-F4, Tab-Ctrl)", "Zone 2 (2-4, E R, S D X)", "Zone 3 (F5-F8, 5 6, T F G C V)", "Zone 4 (7, Y H, B N, Space)", "Zone 5 (8 9, U I J K M)", "Zone 6 (0, O P L ;, , . /, Alt)", "Zone 7 (- = Backspace, [ ] \\, Enter, Shift, Fn)", "Zone 8 (F9-F12, Nav, Arrows)", "Zone 9 (Numpad Left)", "Zone 10 (Numpad Right)", ]; const vLedPositions = [ [1, 3], [3, 3], [5, 3], [7, 3], [9, 3], [11, 3], [13, 3], [15, 3], [17, 3], [19, 3], ]; export function LedNames() { return vLedNames; } export function LedPositions() { return vLedPositions; } // Protocol (0xFF90 vendor interface, 64-byte reports, report ID 0x44): // 44 02 00 + up to 20 x RGB colour data // 44 01 apply ("commit") the frame // The commit must come AFTER the data. These are live writes; nothing is // saved to the keyboard. Without fresh frames the keyboard eventually falls // back to its onboard effect, so we re-send periodically even when idle. const KEEPALIVE_MS = 500; let lastPacket = []; let lastSendTime = 0; export function Initialize() { lastPacket = []; lastSendTime = 0; } export function Render() { sendColors(); } export function Shutdown(SystemSuspending) { const color = SystemSuspending ? "#000000" : shutdownColor; sendColors(color, true); } function sendColors(overrideColor, force = false) { const packet = [0x44, 0x02, 0x00, 0x00]; for (let iIdx = 0; iIdx < vLedPositions.length; iIdx++) { let color; if (overrideColor) { color = hexToRgb(overrideColor); } else if (LightingMode === "Forced") { color = hexToRgb(forcedColor); } else { color = device.color(vLedPositions[iIdx][0], vLedPositions[iIdx][1]); } packet.push(color[0], color[1], color[2]); } const now = Date.now(); if (!force && arraysEqual(packet, lastPacket) && now - lastSendTime < KEEPALIVE_MS) { return; } device.write(packet, 64); device.write([0x44, 0x01, 0x01], 64); lastPacket = packet; lastSendTime = now; } function arraysEqual(a, b) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) { return false; } } return true; } 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 === 2 && endpoint.usage === 0xFF00 && endpoint.usage_page === 0xFF90; } export function ImageUrl() { return "https://assets.signalrgb.com/devices/default/keyboards/full-size-keyboard-render.png"; }