-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathkeyboard.ts
134 lines (116 loc) · 3.66 KB
/
keyboard.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { BrightnessPacket } from "./internal/models/packets/brightness-packet";
import { FirmwarePacket } from "./internal/models/packets/firmware-packet";
import { FreezePacket } from "./internal/models/packets/freeze-packet";
import { InitializePacket } from "./internal/models/packets/initialize-packet";
import { TriggerPacket } from "./internal/models/packets/trigger-packet";
import { KeyState } from "./key-state";
import { USBHID } from "./usb/hid";
import { Usb } from "./usb/usb";
export class Keyboard {
private interface: number;
private vendorId: number;
private productId: number;
private usage: number;
private usbDevice: Usb | undefined;
private sequence: number = 0;
constructor(vendorId?: number, productId?: number, deviceInterface?: number, usage?: number) {
this.vendorId = vendorId || 0x24f0;
this.productId = productId || 0x2020;
this.interface = deviceInterface || 2;
this.usage = usage || 165;
}
public find(): Usb {
this.usbDevice = new USBHID();
this.usbDevice.connect(this.vendorId, this.productId, this.interface, this.usage);
return this.usbDevice;
}
/**
* Initializes the keyboard so we can communicate with it.
*/
public initialize() {
if (this.usbDevice === undefined) {
throw new Error("No HID keyboard device has been found.");
}
this.featureReports(new InitializePacket().buildPacketBytes());
}
/**
* Sets an array of KeyStates to the keyboard
*/
public setKeyState(states: KeyState[] | KeyState) {
if (Array.isArray(states)) {
for (const state of states) {
this.executePackets(state);
}
} else {
this.executePackets(states);
}
}
/**
* Sets the brightness of the keyboard
* @param brightness 0 - 63
*/
public setBrightness(brightness: number) {
this.featureReports(new BrightnessPacket(brightness).buildPacketBytes());
}
/**
* Freezes the current effects on the keyboard
* @param brightness 0 - 63
*/
public freezeEffects() {
this.featureReports(new FreezePacket().buildPacketBytes());
}
/**
* Executes any pending color commands on the keyboard.
*/
public apply() {
this.featureReports(new TriggerPacket().buildPacketBytes());
}
public getKeyboardData() {
this.featureReports(new FirmwarePacket().buildPacketBytes());
const fwVer = this.readDataFromDevice();
return {
firmware: fwVer[4] + "." + fwVer[5] + "." + fwVer[6] + "." + fwVer[7],
packetCount: fwVer[3],
};
}
/**
* Disconnects from the keyboard. Does nothing if already disconnected or not initialized.
*/
public close() {
if (typeof this.usbDevice !== "undefined") {
this.usbDevice.disconnect();
this.usbDevice = undefined;
}
}
private executePackets(state: KeyState) {
for (const aPacket of state.build()) {
this.featureReports(aPacket);
}
}
private featureReports(report: number[]) {
const buff = [0 /* report id */, ...report];
while (buff.length < 65) {
buff.push(0);
}
buff[3] = this.sequence;
this.writeDataToDevice(buff);
const res = this.readDataFromDevice();
if (res[2] !== 0x14 || res[3] !== this.sequence) {
throw new Error("no ack " + this.sequence);
}
this.sequence++;
if (this.sequence > 0xFF) { this.sequence = 0x00; }
}
private readDataFromDevice(): number[] {
if (typeof this.usbDevice === "undefined") {
throw new Error("Not connected to device");
}
return this.usbDevice.read();
}
private writeDataToDevice(data: number[]) {
if (typeof this.usbDevice === "undefined") {
throw new Error("Not connected to device");
}
this.usbDevice.write(data);
}
}