mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-21 15:25:22 +03:00
Merge bbf4a01723
into be45ddf05e
This commit is contained in:
commit
dec455a103
3 changed files with 74 additions and 44 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue