-
Version 2.15 (2025-09-13) - Support this project
+
Version 2.16 beta 4 (2025-09-09) - Support this project
diff --git a/js/controllers/base-controller.js b/js/controllers/base-controller.js
index 4e797ef..5d92c16 100644
--- a/js/controllers/base-controller.js
+++ b/js/controllers/base-controller.js
@@ -54,11 +54,12 @@ class BaseController {
*/
alloc_req(id, data = []) {
const fr = this.device.collections[0].featureReports;
- const report = fr.find(e => e.reportId === id);
+ const [report] = fr.find(e => e.reportId === id)?.items || [];
const maxLen = report?.reportCount || data.length;
- const out = new Uint8Array(data.length);
- out.set(data.slice(0, maxLen));
+ const len = Math.min(data.length, maxLen);
+ const out = new Uint8Array(maxLen);
+ out.set(data.slice(0, len));
return out;
}
@@ -72,7 +73,13 @@ class BaseController {
if (Array.isArray(data)) {
data = this.alloc_req(reportId, data);
}
- return await this.device.sendFeatureReport(reportId, data);
+
+ try {
+ return await this.device.sendFeatureReport(reportId, data);
+ } catch (error) {
+ // HID doesn't throw proper Errors with stack (stack is "name: message") so generate a new stack here
+ throw new Error(error.stack);
+ }
}
/**
diff --git a/js/controllers/ds5-controller.js b/js/controllers/ds5-controller.js
index 0b14b39..e85f731 100644
--- a/js/controllers/ds5-controller.js
+++ b/js/controllers/ds5-controller.js
@@ -95,7 +95,9 @@ class DS5Controller extends BaseController {
const { l } = this;
// Device-only: collect info and return a common structure; do not touch the DOM
try {
+ console.log("Fetching DS5 info...");
const view = lf("ds5_info", await this.receiveFeatureReport(0x20));
+ console.log("Got DS5 info report:", buf2hex(view.buffer));
const cmd = view.getUint8(0, true);
if(cmd != 0x20 || view.buffer.byteLength != 64)
return { ok: false, error: new Error("Invalid response for ds5_info") };
@@ -160,6 +162,7 @@ class DS5Controller extends BaseController {
return { ok: true, infoItems, nv, disable_bits, pending_reboot };
} catch(e) {
la("ds5_info_error", {"r": e})
+ console.error(e.stack);
return { ok: false, error: e, disable_bits: 1 };
}
}
diff --git a/js/core.js b/js/core.js
index 367650b..e6f76b8 100644
--- a/js/core.js
+++ b/js/core.js
@@ -51,7 +51,7 @@ function gboot() {
show_popup(event.error?.message || event.message);
});
- window.addEventListener("unhandledRejection", (event) => {
+ window.addEventListener("unhandledrejection", (event) => {
console.error("Unhandled rejection:", event.reason?.stack || event.reason);
close_all_modals();
show_popup(event.reason?.message || event.reason);
@@ -105,13 +105,15 @@ async function connect() {
}
if (devices.length > 1) {
- $("#btnconnect").prop("disabled", false);
- $("#connectspinner").hide();
throw new Error(l("Please connect only one controller at time."));
}
- const device = devices[0];
- if(device.opened) await device.close();
+ const [device] = devices;
+ if(device.opened) {
+ console.log("Device already opened, closing it before re-opening.");
+ await device.close();
+ await sleep(500);
+ }
await device.open();
la("connect", {"p": device.productId, "v": device.vendorId});
@@ -126,6 +128,7 @@ async function connect() {
async function continue_connection({data, device}) {
try {
if (!controller || controller.isConnected()) {
+ console.log("Already connected. Reset input report handler.");
controller?.setInputReportHandler(null);
return;
}
@@ -135,9 +138,6 @@ async function continue_connection({data, device}) {
// Detect if the controller is connected via USB
const reportLen = data.byteLength;
if(reportLen != 63) {
- $("#btnconnect").prop("disabled", false);
- $("#connectspinner").hide();
- await disconnect();
throw new Error(l("Please connect the device using a USB cable."));
}
@@ -159,9 +159,6 @@ async function continue_connection({data, device}) {
info = await controllerInstance.getInfo();
} catch (error) {
- $("#btnconnect").prop("disabled", false);
- $("#connectspinner").hide();
- await disconnect();
if (device) {
throw new Error(l("Connected invalid device: ") + dec2hex(device.vendorId) + ":" + dec2hex(device.productId));
} else {
@@ -171,11 +168,8 @@ async function continue_connection({data, device}) {
if(!info?.ok) {
// Not connected/failed to fetch info
- $("#btnconnect").prop("disabled", false);
- $("#connectspinner").hide();
- await disconnect();
- if(info) console.error(info.error);
- throw new Error(l("Connected invalid device: ") + l("Error 1"));
+ if(info) console.error(JSON.stringify(info, null, 2));
+ throw new Error(l("Connected invalid device: ") + l("Error 1") + (info?.error ? ` (${info.error}).` : '. '));
}
connected = true;
@@ -184,6 +178,7 @@ async function continue_connection({data, device}) {
applyDeviceUI(ui);
// Assign input processor for stream
+ console.log("Setting input report handler.");
device.oninputreport = controller.getInputHandler();
const deviceName = ControllerFactory.getDeviceName(device.productId);
@@ -203,9 +198,6 @@ async function continue_connection({data, device}) {
// Edge-specific: pending reboot check (from nv)
if (model == "DS5_Edge" && info?.pending_reboot) {
- $("#btnconnect").prop("disabled", false);
- $("#connectspinner").hide();
- await disconnect();
throw new Error(l("A reboot is needed to continue using this DualSense Edge. Please disconnect and reconnect your controller."));
}
@@ -236,6 +228,9 @@ async function continue_connection({data, device}) {
if(model == "DS5_Edge") {
show_edge_modal();
}
+ } catch(err) {
+ await disconnect();
+ throw err;
} finally {
$("#btnconnect").prop("disabled", false);
$("#connectspinner").hide();
diff --git a/js/modals/finetune-modal.js b/js/modals/finetune-modal.js
index 7c0c43b..92c33cd 100644
--- a/js/modals/finetune-modal.js
+++ b/js/modals/finetune-modal.js
@@ -285,7 +285,7 @@ export class Finetune {
}
async _readFinetuneData() {
- const data = await ds5_get_inmemory_module_data(); //mm there's also a missing await here
+ const data = await this.controller.getInMemoryModuleData();
if(!data) {
throw new Error("ERROR: Cannot read calibration data");
}