dualsense-ts
Version:
A natural interface for your DualSense controller, with Typescript
129 lines • 4.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DualsenseHID = void 0;
const hid_provider_1 = require("./hid_provider");
const SCOPE_A = 1;
const SCOPE_B = 2;
/** Coordinates a HIDProvider and tracks the latest HID state */
class DualsenseHID {
constructor(provider, refreshRate = 30) {
this.provider = provider;
/** Subscribers waiting for HID state updates */
this.subscribers = new Set();
/** Subscribers waiting for error updates */
this.errorSubscribers = new Set();
/** Queue of pending HID commands */
this.pendingCommands = [];
/** Most recent HID state of the device */
this.state = { ...hid_provider_1.DefaultDualsenseHIDState };
provider.onData = this.set.bind(this);
provider.onError = this.handleError.bind(this);
setInterval(() => {
if (this.pendingCommands.length > 0) {
(async () => {
const command = [...this.pendingCommands];
this.pendingCommands = [];
await provider.write(DualsenseHID.buildFeatureReport(command));
})().catch((err) => {
this.handleError(new Error(`HID write failed: ${JSON.stringify(err)}`));
});
}
}, 1000 / refreshRate);
}
/** Register a handler for HID state updates */
register(callback) {
this.subscribers.add(callback);
}
/** Cancel a previously registered handler */
unregister(callback) {
this.subscribers.delete(callback);
}
/** Add a subscriber for errors */
on(type, callback) {
if (type === "error")
this.errorSubscribers.add(callback);
}
/** Update the HID state and pass it along to all state subscribers */
set(state) {
this.state = state;
this.subscribers.forEach((callback) => callback(state));
}
/** Pass errors along to all error subscribers */
handleError(error) {
this.errorSubscribers.forEach((callback) => callback(error));
}
/** Condense all pending commands into one HID feature report */
static buildFeatureReport(events) {
const report = new Uint8Array(46).fill(0);
report[0] = 0x2;
report[1] = events
.filter(({ scope: { index } }) => index === SCOPE_A)
.reduce((acc, { scope: { value } }) => {
return acc | value;
}, 0x00);
report[2] = events
.filter(({ scope: { index } }) => index === SCOPE_B)
.reduce((acc, { scope: { value } }) => {
return acc | value;
}, 0x00);
events.forEach(({ values }) => {
values.forEach(({ index, value }) => {
report[index] = value;
});
});
return report;
}
/** Set intensity for left and right rumbles */
setRumble(left, right) {
this.pendingCommands.push({
scope: {
index: SCOPE_A,
value: 2 /* CommandScopeA.PrimaryRumble */ | 1 /* CommandScopeA.HapticRumble */,
},
values: [
{ index: 3, value: right },
{ index: 4, value: left },
],
});
this.pendingCommands.push({
scope: { index: SCOPE_B, value: 64 /* CommandScopeB.MotorPower */ },
values: [],
});
}
/** Set left trigger resistance and behavior */
setLeftTriggerFeedback(mode, forces) {
this.pendingCommands.push({
scope: { index: SCOPE_A, value: 8 /* CommandScopeA.LeftTriggerFeedback */ },
values: [
{ index: 22, value: mode },
...forces.map((force, index) => ({ index: 23 + index, value: force })),
],
});
}
/** Set right trigger resistance and behavior */
setRightTriggerFeedback(mode, forces) {
this.pendingCommands.push({
scope: { index: SCOPE_A, value: 4 /* CommandScopeA.RightTriggerFeedback */ },
values: [
{ index: 11, value: mode },
...forces.map((force, index) => ({ index: 12 + index, value: force })),
],
});
}
/** Set microphone LED brightness */
setMicrophoneLED(brightness) {
this.pendingCommands.push({
scope: { index: SCOPE_B, value: 1 /* CommandScopeB.MicrophoneLED */ },
values: [{ index: 9, value: brightness }],
});
}
/** Set player ID LEDs */
setPlayerId(id) {
this.pendingCommands.push({
scope: { index: SCOPE_B, value: 16 /* CommandScopeB.PlayerLeds */ },
values: [{ index: 44, value: id }],
});
}
}
exports.DualsenseHID = DualsenseHID;
//# sourceMappingURL=dualsense_hid.js.map