1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-21 15:25:22 +03:00
This commit is contained in:
Vitroid 2025-07-16 17:23:48 +02:00 committed by GitHub
commit dec455a103
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 74 additions and 44 deletions

View file

@ -334,18 +334,26 @@ class WebSerial extends EventTarget {
// AT32 on macOS requires smaller chunks (63 bytes) to work correctly due to
// USB buffer size limitations in the macOS implementation
const batchWriteSize = 63;
let remainingData = data;
while (remainingData.byteLength > batchWriteSize) {
const sliceData = remainingData.slice(0, batchWriteSize);
remainingData = remainingData.slice(batchWriteSize);
// Ensure data is a Uint8Array for proper slicing
const dataArray = data instanceof Uint8Array ? data : new Uint8Array(data);
let offset = 0;
while (offset + batchWriteSize < dataArray.byteLength) {
const chunk = dataArray.slice(offset, offset + batchWriteSize);
offset += batchWriteSize;
try {
await this.writer.write(sliceData);
await this.writer.write(chunk);
} catch (error) {
console.error(`${logHead} Error writing batch chunk:`, error);
throw error; // Re-throw to be caught by the send method
}
}
await this.writer.write(remainingData);
// Write the remaining data
if (offset < dataArray.byteLength) {
await this.writer.write(dataArray.slice(offset));
}
}
async send(data, callback) {
@ -358,14 +366,17 @@ class WebSerial extends EventTarget {
}
try {
if (this.isNeedBatchWrite) {
await this.batchWrite(data);
} else {
await this.writer.write(data);
}
this.bytesSent += data.byteLength;
// Create a buffer from the data
const buffer = data instanceof ArrayBuffer ? data : new Uint8Array(data).buffer;
const result = { bytesSent: data.byteLength };
if (this.isNeedBatchWrite) {
await this.batchWrite(buffer);
} else {
await this.writer.write(new Uint8Array(buffer));
}
this.bytesSent += buffer.byteLength;
const result = { bytesSent: buffer.byteLength };
if (callback) {
callback(result);
}

View file

@ -14,6 +14,7 @@ import $ from "jquery";
import { ispConnected } from "../utils/connection";
import { sensorTypes } from "../sensor_types";
import { addArrayElementsAfter, replaceArrayElement } from "../utils/array";
import { isFirefoxBrowser } from "../utils/checkBrowserCompatibility";
const setup = {
yaw_fix: 0.0,
@ -387,17 +388,19 @@ setup.initialize = function (callback) {
const buildConfig = `<span class="buildInfoBtn" title="${i18n.getMessage(
"initialSetupInfoBuildConfig",
)}: ${buildRoot}/json">
<a href="${buildRoot}/json" target="_blank"><strong>${i18n.getMessage(
"initialSetupInfoBuildConfig",
)}</strong></a></span>`;
<a href="${buildRoot}/json" target="_blank">
<strong>${i18n.getMessage("initialSetupInfoBuildConfig")}</strong>
</a>
</span>`;
// Creates the "Log" button
const buildLog = `<span class="buildInfoBtn" title="${i18n.getMessage(
"initialSetupInfoBuildLog",
)}: ${buildRoot}/log">
<a href="${buildRoot}/log" target="_blank"><strong>${i18n.getMessage(
"initialSetupInfoBuildLog",
)}</strong></a></span>`;
<a href="${buildRoot}/log" target="_blank">
<strong>${i18n.getMessage("initialSetupInfoBuildLog")}</strong>
</a>
</span>`;
// Shows the "Config" and "Log" buttons
build_info_e.html(`${buildConfig} ${buildLog}`);
@ -429,19 +432,19 @@ setup.initialize = function (callback) {
// Creates the "Options" button (if possible)
const buildOptions = buildOptionsValid
? `<span class="buildInfoBtn" title="${i18n.getMessage("initialSetupInfoBuildOptionList")}">
<a class="buildOptions" href="#"><strong>${i18n.getMessage(
"initialSetupInfoBuildOptions",
)}</strong></a></span>`
<a class="buildOptions" href="#">
<strong>${i18n.getMessage("initialSetupInfoBuildOptions")}</strong>
</a>
</span>`
: "";
// Creates the "Download" button (if possible)
const buildDownload = buildKeyValid
? `<span class="buildInfoBtn" title="${i18n.getMessage(
"initialSetupInfoBuildDownload",
)}: ${buildRoot}/hex">
<a href="${buildRoot}/hex" target="_blank"><strong>${i18n.getMessage(
"initialSetupInfoBuildDownload",
)}</strong></a></span>`
? `<span class="buildInfoBtn" title="${i18n.getMessage("initialSetupInfoBuildDownload")}: ${buildRoot}/hex">
<a href="${buildRoot}/hex" target="_blank">
<strong>${i18n.getMessage("initialSetupInfoBuildDownload")}</strong>
</a>
</span>`
: "";
// Shows the "Options" and/or "Download" buttons
@ -493,13 +496,14 @@ setup.initialize = function (callback) {
}
function showNetworkStatus() {
const isFirefox = isFirefoxBrowser();
const networkStatus = ispConnected();
let statusText = "";
const type = navigator.connection.effectiveType;
const downlink = navigator.connection.downlink;
const rtt = navigator.connection.rtt;
const type = isFirefox ? "NA" : navigator.connection.effectiveType;
const downlink = isFirefox ? "NA" : navigator.connection.downlink;
const rtt = isFirefox ? "NA" : navigator.connection.rtt;
if (!networkStatus || !navigator.onLine || type === "none") {
statusText = i18n.getMessage("initialSetupNetworkInfoStatusOffline");
@ -510,9 +514,9 @@ setup.initialize = function (callback) {
}
$(".network-status").text(statusText);
$(".network-type").text(navigator.connection.effectiveType);
$(".network-downlink").text(`${navigator.connection.downlink} Mbps`);
$(".network-rtt").text(navigator.connection.rtt);
$(".network-type").text(type);
$(".network-downlink").text(`${downlink} Mbps`);
$(".network-rtt").text(rtt);
}
prepareDisarmFlags();

View file

@ -28,15 +28,26 @@ export function getOS() {
}
export function isChromiumBrowser() {
if (navigator.userAgentData) {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
if (!navigator.userAgentData) {
// Fallback to traditional userAgent string check
return /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
}
// https://learn.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-guidance
return navigator.userAgentData.brands.some((brand) => {
return brand.brand == "Chromium";
});
}
// Fallback for older browsers/Android
const ua = navigator.userAgent.toLowerCase();
return ua.includes("chrom") || ua.includes("edg");
export function isFirefoxBrowser() {
if (navigator.userAgentData) {
return navigator.userAgentData.brands.some((brand) => {
return brand.brand == "Firefox";
});
}
// Fallback to traditional userAgent string check
return navigator.userAgent.includes("Firefox");
}
export function isAndroid() {
@ -65,15 +76,18 @@ export function checkBrowserCompatibility() {
const isWebBluetooth = checkWebBluetoothSupport();
const isWebUSB = checkWebUSBSupport();
const isChromium = isChromiumBrowser();
const isFirefox = isFirefoxBrowser();
const isNative = Capacitor.isNativePlatform();
const compatible = isNative || (isChromium && (isWebSerial || isWebBluetooth || isWebUSB));
const compatible = isNative || ((isChromium || isFirefox) && (isWebSerial || isWebBluetooth || isWebUSB));
console.log("User Agent: ", navigator.userAgentData);
console.log("Native: ", isNative);
console.log("Chromium: ", isChromium);
console.log("Firefox: ", isFirefox);
console.log("Web Serial: ", isWebSerial);
console.log("Web Bluetooth: ", isWebBluetooth);
console.log("Web USB: ", isWebUSB);
console.log("OS: ", getOS());
console.log("Android: ", isAndroid());
@ -86,7 +100,8 @@ export function checkBrowserCompatibility() {
let errorMessage = "";
if (!isChromium) {
errorMessage = "Betaflight app requires a Chromium based browser (Chrome, Chromium, Edge).<br/>";
errorMessage =
"Betaflight app requires a Chromium based browser (Chrome, Chromium, Edge),<br> or Firefox based browser running the <a href='https://addons.mozilla.org/firefox/addon/webserial-for-firefox/'>WebSerial extension</a>.<br/>";
}
if (!isWebBluetooth) {