1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-18 22:05:13 +03:00

Serial refactor part 2 (#4404)

* Serial refactor part 2

* Fix callback

* Fix some sonar complaints

* Does no longer block dfu

* Excempt presets

* Add interval for reboot dialog

* Update message after review Vitroid
This commit is contained in:
Mark Haslinghuis 2025-03-31 22:54:06 +02:00 committed by GitHub
parent c842b9d60b
commit 2d36e62474
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 402 additions and 135 deletions

View file

@ -320,6 +320,12 @@
"configuratorUpdateWebsite": { "configuratorUpdateWebsite": {
"message": "Go to Release Website" "message": "Go to Release Website"
}, },
"rebootFlightController": {
"message": "Rebooting flight controller, reconnect when ready"
},
"rebootFlightControllerReady": {
"message": "Flight Controller is ready"
},
"deviceRebooting": { "deviceRebooting": {
"message": "Device - <span class=\"message-negative\">Rebooting</span>" "message": "Device - <span class=\"message-negative\">Rebooting</span>"
}, },

View file

@ -2,7 +2,6 @@ import { get as getConfig } from "./ConfigStorage";
import { EventBus } from "../components/eventBus"; import { EventBus } from "../components/eventBus";
import { serial } from "./serial.js"; import { serial } from "./serial.js";
import WEBUSBDFU from "./protocols/webusbdfu"; import WEBUSBDFU from "./protocols/webusbdfu";
import WebBluetooth from "./protocols/WebBluetooth.js";
import { reactive } from "vue"; import { reactive } from "vue";
const DEFAULT_PORT = "noselection"; const DEFAULT_PORT = "noselection";
@ -35,44 +34,42 @@ const PortHandler = new (function () {
})(); })();
PortHandler.initialize = function () { PortHandler.initialize = function () {
EventBus.$on("ports-input:request-permission-bluetooth", this.askBluetoothPermissionPort.bind(this)); EventBus.$on("ports-input:request-permission-bluetooth", () => this.requestDevicePermission("bluetooth"));
EventBus.$on("ports-input:request-permission", this.askSerialPermissionPort.bind(this)); EventBus.$on("ports-input:request-permission", () => this.requestDevicePermission("serial"));
EventBus.$on("ports-input:change", this.onChangeSelectedPort.bind(this)); EventBus.$on("ports-input:change", this.onChangeSelectedPort.bind(this));
// Use serial for all protocol events // Use serial for all protocol events
serial.addEventListener("addedDevice", (event) => { serial.addEventListener("addedDevice", (event) => {
const detail = event.detail; const detail = event.detail;
// Determine the device type based on its properties
if (detail?.path?.startsWith("bluetooth")) { if (detail?.path?.startsWith("bluetooth")) {
this.addedBluetoothDevice(detail); this.handleDeviceAdded(detail, "bluetooth");
} else if (detail?.path?.startsWith("usb_")) {
this.addedUsbDevice(detail);
} else { } else {
this.addedSerialDevice(detail); this.handleDeviceAdded(detail, "serial");
} }
}); });
serial.addEventListener("removedDevice", (event) => { serial.addEventListener("removedDevice", (event) => {
const detail = event.detail; this.removedSerialDevice(event.detail);
// Determine the device type based on its properties
if (detail?.path?.startsWith("bluetooth")) {
this.removedBluetoothDevice(detail);
} else if (detail?.path?.startsWith("usb_")) {
// Handle USB device removal if needed
} else {
this.removedSerialDevice(detail);
}
}); });
// Keep USB listener separate as it's not part of the serial protocols // Keep USB listener separate as it's not part of the serial protocols
WEBUSBDFU.addEventListener("addedDevice", (event) => this.addedUsbDevice(event.detail)); WEBUSBDFU.addEventListener("addedDevice", (event) => this.addedUsbDevice(event.detail));
// Initial device discovery // Initial device discovery using the serial facade
this.addedSerialDevice(); this.refreshAllDeviceLists();
this.addedBluetoothDevice(); };
this.addedUsbDevice();
// Refactored refreshAllDeviceLists to use updateDeviceList
PortHandler.refreshAllDeviceLists = async function () {
// Update all device lists in parallel
return Promise.all([
this.updateDeviceList("serial"),
this.updateDeviceList("bluetooth"),
this.updateDeviceList("usb"),
]).then(() => {
this.selectActivePort();
});
}; };
PortHandler.setShowVirtualMode = function (showVirtualMode) { PortHandler.setShowVirtualMode = function (showVirtualMode) {
@ -89,44 +86,40 @@ PortHandler.setShowAllSerialDevices = function (showAllSerialDevices) {
this.showAllSerialDevices = showAllSerialDevices; this.showAllSerialDevices = showAllSerialDevices;
}; };
PortHandler.addedSerialDevice = function (device) {
this.updateCurrentSerialPortsList().then(() => {
const selectedPort = this.selectActivePort(device);
if (!device || selectedPort === device.path) {
// Send this event when the port handler auto selects a new device
EventBus.$emit("port-handler:auto-select-serial-device", selectedPort);
}
});
};
PortHandler.removedSerialDevice = function (device) { PortHandler.removedSerialDevice = function (device) {
this.updateCurrentSerialPortsList().then(() => { console.log(`${this.logHead} Device removal event received:`, device);
if (this.portPicker.selectedPort === device.path) {
this.selectActivePort();
}
});
};
PortHandler.addedBluetoothDevice = function (device) { // Get device path safely
this.updateCurrentBluetoothPortsList().then(() => { const devicePath = device?.path || (typeof device === "string" ? device : null);
const selectedPort = this.selectActivePort(device);
if (!device || selectedPort === device.path) {
// Send this event when the port handler auto selects a new device
EventBus.$emit("port-handler:auto-select-bluetooth-device", selectedPort);
}
});
};
PortHandler.removedBluetoothDevice = function (device) { if (!devicePath) {
this.updateCurrentBluetoothPortsList().then(() => { console.warn(`${this.logHead} Device removal event missing path information`, device);
if (this.portPicker.selectedPort === device.path) { // Still update ports, but don't try to use the undefined path
this.updateDeviceList("serial").then(() => {
this.selectActivePort(); this.selectActivePort();
});
return;
}
// Update the appropriate ports list based on the device type
const updatePromise = devicePath.startsWith("bluetooth")
? this.updateDeviceList("bluetooth")
: this.updateDeviceList("serial");
const wasSelectedPort = this.portPicker.selectedPort === devicePath;
updatePromise.then(() => {
if (wasSelectedPort) {
this.selectActivePort();
// Send event for UI components that might need to update
EventBus.$emit("port-handler:device-removed", devicePath);
} }
}); });
}; };
PortHandler.addedUsbDevice = function (device) { PortHandler.addedUsbDevice = function (device) {
this.updateCurrentUsbPortsList().then(() => { this.updateDeviceList("usb").then(() => {
const selectedPort = this.selectActivePort(device); const selectedPort = this.selectActivePort(device);
if (!device || selectedPort === device.path) { if (!device || selectedPort === device.path) {
// Send this event when the port handler auto selects a new device // Send this event when the port handler auto selects a new device
@ -139,30 +132,28 @@ PortHandler.onChangeSelectedPort = function (port) {
this.portPicker.selectedPort = port; this.portPicker.selectedPort = port;
}; };
PortHandler.updateCurrentSerialPortsList = async function () { /**
const ports = await serial.getDevices(); * Request permission for a device of the specified type
const orderedPorts = this.sortPorts(ports); * @param {string} deviceType - Type of device ('serial' or 'bluetooth')
this.portAvailable = orderedPorts.length > 0; */
console.log(`${this.logHead} Found serial port`, orderedPorts); PortHandler.requestDevicePermission = function (deviceType = "serial") {
this.currentSerialPorts = [...orderedPorts]; // Determine whether to show all devices based on device type
}; const showAllDevices = deviceType === "serial" ? this.showAllSerialDevices : false;
PortHandler.updateCurrentUsbPortsList = async function () { // Use serial facade to request permission
const ports = await WEBUSBDFU.getDevices(); serial
const orderedPorts = this.sortPorts(ports); .requestPermissionDevice(showAllDevices, deviceType)
this.dfuAvailable = orderedPorts.length > 0; .then((port) => {
console.log(`${this.logHead} Found DFU port`, orderedPorts); if (port) {
this.currentUsbPorts = [...orderedPorts]; console.log(`${this.logHead} Permission granted for ${deviceType} device:`, port);
}; this.selectActivePort(port);
} else {
PortHandler.updateCurrentBluetoothPortsList = async function () { console.log(`${this.logHead} Permission request cancelled or failed for ${deviceType}`);
if (WebBluetooth.bluetooth) { }
const ports = await WebBluetooth.getDevices(); })
const orderedPorts = this.sortPorts(ports); .catch((error) => {
this.bluetoothAvailable = orderedPorts.length > 0; console.error(`${this.logHead} Error requesting permission for ${deviceType}:`, error);
console.log(`${this.logHead} Found bluetooth port`, orderedPorts); });
this.currentBluetoothPorts = [...orderedPorts];
}
}; };
PortHandler.sortPorts = function (ports) { PortHandler.sortPorts = function (ports) {
@ -174,31 +165,11 @@ PortHandler.sortPorts = function (ports) {
}); });
}; };
PortHandler.askBluetoothPermissionPort = function () { PortHandler.selectActivePort = function (suggestedDevice = false) {
if (WebBluetooth.bluetooth) {
WebBluetooth.requestPermissionDevice().then((port) => {
// When giving permission to a new device, the port is selected in the handleNewDevice method, but if the user
// selects a device that had already permission, or cancels the permission request, we need to select the port
// so do it here too
this.selectActivePort(port);
});
}
};
PortHandler.askSerialPermissionPort = function () {
serial.requestPermissionDevice(this.showAllSerialDevices).then((port) => {
// When giving permission to a new device, the port is selected in the handleNewDevice method, but if the user
// selects a device that had already permission, or cancels the permission request, we need to select the port
// so do it here too
this.selectActivePort(port);
});
};
PortHandler.selectActivePort = function (suggestedDevice) {
const deviceFilter = ["AT32", "CP210", "SPR", "STM"]; const deviceFilter = ["AT32", "CP210", "SPR", "STM"];
let selectedPort; let selectedPort;
// Return the same that is connected to serial // First check for active connections
if (serial.connected) { if (serial.connected) {
selectedPort = this.currentSerialPorts.find((device) => device === serial.getConnectedPort()); selectedPort = this.currentSerialPorts.find((device) => device === serial.getConnectedPort());
} }
@ -208,10 +179,22 @@ PortHandler.selectActivePort = function (suggestedDevice) {
selectedPort = this.currentUsbPorts.find((device) => device === WEBUSBDFU.getConnectedPort()); selectedPort = this.currentUsbPorts.find((device) => device === WEBUSBDFU.getConnectedPort());
} }
// Return the same that is connected to bluetooth // If there is a connection, return it
if (WebBluetooth.device) { // if (selectedPort) {
selectedPort = this.currentBluetoothPorts.find((device) => device === WebBluetooth.getConnectedPort()); // console.log(`${this.logHead} Using connected device: ${selectedPort.path}`);
} // selectedPort = selectedPort.path;
// return selectedPort;
// }
// If there is no connection, check for the last used device
// Check if the device is already connected
// if (this.portPicker.selectedPort && this.portPicker.selectedPort !== DEFAULT_PORT) {
// selectedPort = this.currentSerialPorts.find((device) => device.path === this.portPicker.selectedPort);
// if (selectedPort) {
// console.log(`${this.logHead} Using previously selected device: ${selectedPort.path}`);
// return selectedPort.path;
// }
// }
// Return the suggested device (the new device that has been detected) // Return the suggested device (the new device that has been detected)
if (!selectedPort && suggestedDevice) { if (!selectedPort && suggestedDevice) {
@ -269,6 +252,81 @@ PortHandler.selectActivePort = function (suggestedDevice) {
return selectedPort; return selectedPort;
}; };
// Create a unified handler for device addition
PortHandler.handleDeviceAdded = function (device, deviceType) {
if (!device) {
console.warn(`${this.logHead} Invalid ${deviceType} device added event`);
return;
}
console.log(`${this.logHead} ${deviceType} device added:`, device);
// Update the appropriate device list
const updatePromise =
deviceType === "bluetooth" ? this.updateDeviceList("bluetooth") : this.updateDeviceList("serial");
updatePromise.then(() => {
const selectedPort = this.selectActivePort(device);
if (selectedPort === device.path) {
// Emit an event with the proper type for backward compatibility
EventBus.$emit(`port-handler:auto-select-${deviceType}-device`, selectedPort);
}
});
};
/**
* Update device list with common implementation
* @param {string} deviceType - Type of device ('serial', 'bluetooth', 'usb')
* @returns {Promise} - Promise that resolves after updating the ports list
*/
PortHandler.updateDeviceList = async function (deviceType) {
let ports = [];
try {
switch (deviceType) {
case "bluetooth":
ports = await serial.getDevices("bluetooth");
break;
case "usb":
ports = await WEBUSBDFU.getDevices();
break;
case "serial":
default:
ports = await serial.getDevices("serial");
break;
}
// Sort the ports
const orderedPorts = this.sortPorts(ports);
// Update the appropriate properties based on device type
switch (deviceType) {
case "bluetooth":
this.bluetoothAvailable = orderedPorts.length > 0;
this.currentBluetoothPorts = [...orderedPorts];
console.log(`${this.logHead} Found bluetooth port(s)`, orderedPorts);
break;
case "usb":
this.dfuAvailable = orderedPorts.length > 0;
this.currentUsbPorts = [...orderedPorts];
console.log(`${this.logHead} Found DFU port(s)`, orderedPorts);
break;
case "serial":
default:
this.portAvailable = orderedPorts.length > 0;
this.currentSerialPorts = [...orderedPorts];
console.log(`${this.logHead} Found serial port(s)`, orderedPorts);
break;
}
return orderedPorts;
} catch (error) {
console.error(`${this.logHead} Error updating ${deviceType} devices:`, error);
return [];
}
};
// We need to explicit make it reactive. If not, Vue3 does not detect correctly changes in array properties // We need to explicit make it reactive. If not, Vue3 does not detect correctly changes in array properties
// like currentSerialPorts, currentUsbPorts, currentBluetoothPorts // like currentSerialPorts, currentUsbPorts, currentBluetoothPorts
export default reactive(PortHandler); export default reactive(PortHandler);

View file

@ -272,7 +272,7 @@ class WebSerial extends EventTarget {
if (this.reader) { if (this.reader) {
await this.reader.cancel(); await this.reader.cancel();
this.reader.releaseLock(); await this.reader.releaseLock();
this.reader = null; this.reader = null;
} }

View file

@ -22,6 +22,14 @@ class Serial extends EventTarget {
this._websocket = new Websocket(); this._websocket = new Websocket();
this._virtual = new VirtualSerial(); this._virtual = new VirtualSerial();
// Update protocol map to use consistent naming
this._protocolMap = {
serial: this._webSerial, // TODO: should be 'webserial'
bluetooth: this._bluetooth, // TODO: should be 'webbluetooth'
websocket: this._websocket,
virtual: this._virtual,
};
// Initialize with default protocol // Initialize with default protocol
this.selectProtocol(false); this.selectProtocol(false);
@ -29,6 +37,42 @@ class Serial extends EventTarget {
this._setupEventForwarding(); this._setupEventForwarding();
} }
// Add a getter method to safely access the protocol map
_getProtocolByType(type) {
if (!type) {
return this._protocol;
}
const protocol = this._protocolMap[type.toLowerCase()];
if (!protocol) {
console.warn(`${this.logHead} Unknown protocol type: ${type}`);
}
return protocol || null;
}
/**
* Get the protocol type as a string
* @param {Object} protocol - Protocol instance
* @returns {string} - Protocol type name
*/
_getProtocolType(protocol) {
if (protocol === this._webSerial) {
return "webserial";
}
if (protocol === this._bluetooth) {
return "webbluetooth";
}
if (protocol === this._websocket) {
return "websocket";
}
if (protocol === this._virtual) {
return "virtual";
}
return "unknown";
}
/** /**
* Set up event forwarding from all protocols to the Serial class * Set up event forwarding from all protocols to the Serial class
*/ */
@ -41,27 +85,20 @@ class Serial extends EventTarget {
events.forEach((eventType) => { events.forEach((eventType) => {
protocol.addEventListener(eventType, (event) => { protocol.addEventListener(eventType, (event) => {
let newDetail; let newDetail;
if (event.type === "receive") {
// Special handling for 'receive' events to ensure data is properly passed through // For 'receive' events, we need to handle the data differently
if (eventType === "receive") {
// If it's already a Uint8Array or ArrayBuffer, keep it as is
newDetail = { newDetail = {
data: event.detail, data: event.detail,
protocolType: protocolType: this._getProtocolType(protocol),
protocol === this._webSerial
? "webSerial"
: protocol === this._bluetooth
? "bluetooth"
: protocol === this._websocket
? "websocket"
: protocol === this._virtual
? "virtual"
: "unknown",
}; };
} else { } else {
// For all other events, pass through the detail as is // For other events, we can use the detail directly
newDetail = event.detail; newDetail = {
...event.detail,
protocolType: this._getProtocolType(protocol),
};
} }
// Dispatch the event with the new detail // Dispatch the event with the new detail
this.dispatchEvent( this.dispatchEvent(
new CustomEvent(event.type, { new CustomEvent(event.type, {
@ -102,14 +139,14 @@ class Serial extends EventTarget {
CONFIGURATOR.bluetoothMode = false; CONFIGURATOR.bluetoothMode = false;
CONFIGURATOR.manualMode = true; CONFIGURATOR.manualMode = true;
} else if (portPath.startsWith("bluetooth")) { } else if (portPath.startsWith("bluetooth")) {
console.log(`${this.logHead} Using bluetooth protocol (based on port path)`); console.log(`${this.logHead} Using bluetooth protocol (based on port path: ${portPath})`);
newProtocol = this._bluetooth; newProtocol = this._bluetooth;
// Update CONFIGURATOR flags for consistency // Update CONFIGURATOR flags for consistency
CONFIGURATOR.virtualMode = false; CONFIGURATOR.virtualMode = false;
CONFIGURATOR.bluetoothMode = true; CONFIGURATOR.bluetoothMode = true;
CONFIGURATOR.manualMode = false; CONFIGURATOR.manualMode = false;
} else { } else {
console.log(`${this.logHead} Using web serial protocol (based on port path)`); console.log(`${this.logHead} Using web serial protocol (based on port path: ${portPath})`);
newProtocol = this._webSerial; newProtocol = this._webSerial;
// Update CONFIGURATOR flags for consistency // Update CONFIGURATOR flags for consistency
CONFIGURATOR.virtualMode = false; CONFIGURATOR.virtualMode = false;
@ -258,17 +295,58 @@ class Serial extends EventTarget {
} }
/** /**
* Get devices from the current protocol * Get devices from a specific protocol type or current protocol
* @param {string} protocolType - Optional protocol type ('serial', 'bluetooth', 'websocket', 'virtual')
* @returns {Promise<Array>} - List of devices
*/ */
async getDevices() { async getDevices(protocolType = null) {
return this._protocol?.getDevices() || []; try {
// Get the appropriate protocol
const targetProtocol = this._getProtocolByType(protocolType);
if (!targetProtocol) {
console.warn(`${this.logHead} No valid protocol for getting devices`);
return [];
}
if (typeof targetProtocol.getDevices !== "function") {
console.error(`${this.logHead} Selected protocol does not implement getDevices`);
return [];
}
return targetProtocol.getDevices() || [];
} catch (error) {
console.error(`${this.logHead} Error getting devices:`, error);
return [];
}
} }
/** /**
* Request permission for a device * Request permission to access a device
* @param {boolean} showAllDevices - Whether to show all devices or only those with filters
* @param {string} protocolType - Optional protocol type ('serial', 'bluetooth', etc.)
* @returns {Promise<Object>} - Promise resolving to the selected device
*/ */
async requestPermissionDevice(showAllSerialDevices = false) { async requestPermissionDevice(showAllDevices = false, protocolType = null) {
return this._protocol?.requestPermissionDevice(showAllSerialDevices) || null; try {
// Get the appropriate protocol
const targetProtocol = this._getProtocolByType(protocolType);
if (!targetProtocol) {
console.warn(`${this.logHead} No valid protocol for permission request`);
return null;
}
if (typeof targetProtocol.requestPermissionDevice !== "function") {
console.error(`${this.logHead} Selected protocol does not support permission requests`);
return null;
}
return targetProtocol.requestPermissionDevice(showAllDevices);
} catch (error) {
console.error(`${this.logHead} Error requesting device permission:`, error);
return null;
}
} }
/** /**

View file

@ -810,12 +810,16 @@ function startLiveDataRefreshTimer() {
export function reinitializeConnection(callback) { export function reinitializeConnection(callback) {
// In virtual mode reconnect when autoconnect is enabled // In virtual mode reconnect when autoconnect is enabled
if (CONFIGURATOR.virtualMode && PortHandler.portPicker.autoConnect) { if (CONFIGURATOR.virtualMode) {
return setTimeout(function () { connectDisconnect();
$("a.connection_button__link").trigger("click"); if (PortHandler.portPicker.autoConnect) {
}, 500); return setTimeout(function () {
$("a.connection_button__link").trigger("click");
}, 500);
}
} }
// Send reboot command to the flight controller
rebootTimestamp = Date.now(); rebootTimestamp = Date.now();
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false); MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false);
@ -824,14 +828,131 @@ export function reinitializeConnection(callback) {
connectDisconnect(); connectDisconnect();
} }
// Show reboot progress modal except for presets tab
if (GUI.active_tab === "presets") {
console.log("Rebooting in presets tab, skipping reboot dialog", GUI.active_tab);
gui_log(i18n.getMessage("deviceRebooting"));
gui_log(i18n.getMessage("deviceReady"));
if (callback && typeof callback === "function") {
callback();
}
return;
}
// Show reboot progress modal
showRebootDialog(callback);
}
function showRebootDialog(callback) {
gui_log(i18n.getMessage("deviceRebooting")); gui_log(i18n.getMessage("deviceRebooting"));
// wait for the device to reboot // Show reboot progress modal
setTimeout(function () { const rebootDialog = document.getElementById("rebootProgressDialog") || createRebootProgressDialog();
gui_log(i18n.getMessage("deviceReady")); rebootDialog.querySelector(".reboot-progress-bar").style.width = "0%";
}, 2000); rebootDialog.querySelector(".reboot-status").textContent = i18n.getMessage("rebootFlightController");
rebootDialog.showModal();
if (callback && typeof callback === "function") { // Update progress during reboot
callback(); let progress = 0;
const progressInterval = setInterval(() => {
progress += 5;
if (progress <= 100) {
rebootDialog.querySelector(".reboot-progress-bar").style.width = `${progress}%`;
}
}, 100);
// Check for successful connection every 100ms
const connectionCheckInterval = setInterval(() => {
if (CONFIGURATOR.connectionValid) {
// Connection established, device is ready
clearInterval(connectionCheckInterval);
clearInterval(progressInterval);
rebootDialog.querySelector(".reboot-progress-bar").style.width = "100%";
rebootDialog.querySelector(".reboot-status").textContent = i18n.getMessage("rebootFlightControllerReady");
// Close the dialog after showing "ready" message briefly
setTimeout(() => {
rebootDialog.close();
}, 1000);
gui_log(i18n.getMessage("deviceReady"));
if (callback && typeof callback === "function") {
callback();
}
}
}, 100);
// Set a maximum timeout for the reboot process (5 seconds)
setTimeout(function () {
clearInterval(connectionCheckInterval);
clearInterval(progressInterval);
rebootDialog.querySelector(".reboot-progress-bar").style.width = "100%";
rebootDialog.querySelector(".reboot-status").textContent = i18n.getMessage("rebootFlightControllerReady");
// Close the dialog after showing "ready" message briefly
setTimeout(() => {
rebootDialog.close();
}, 1000);
gui_log(i18n.getMessage("deviceReady"));
if (callback && typeof callback === "function") {
callback();
}
}, 5000);
// Helper function to create the reboot dialog if it doesn't exist
function createRebootProgressDialog() {
const dialog = document.createElement("dialog");
dialog.id = "rebootProgressDialog";
dialog.className = "dialogReboot";
dialog.innerHTML = `
<div class="content">
<div class="reboot-status">${i18n.getMessage("rebootFlightController")}</div>
<div class="reboot-progress-container">
<div class="reboot-progress-bar"></div>
</div>
</div>
`;
document.body.appendChild(dialog);
// Add styles if not already defined
if (!document.getElementById("rebootProgressStyle")) {
const style = document.createElement("style");
style.id = "rebootProgressStyle";
style.textContent = `
.dialogReboot {
border: 1px solid #3f4241;
border-radius: 5px;
background-color: #2d3233;
color: #fff;
padding: 20px;
max-width: 400px;
}
.reboot-progress-container {
width: 100%;
background-color: #424546;
border-radius: 3px;
margin: 15px 0 5px;
height: 10px;
}
.reboot-progress-bar {
height: 100%;
background-color: #ffbb00;
border-radius: 3px;
transition: width 0.1s ease-in-out;
width: 0%;
}
.reboot-status {
text-align: center;
margin: 10px 0;
}
`;
document.head.appendChild(style);
}
return dialog;
} }
} }

View file

@ -19,6 +19,7 @@ import AutoBackup from "../utils/AutoBackup.js";
import AutoDetect from "../utils/AutoDetect.js"; import AutoDetect from "../utils/AutoDetect.js";
import { EventBus } from "../../components/eventBus"; import { EventBus } from "../../components/eventBus";
import { ispConnected } from "../utils/connection.js"; import { ispConnected } from "../utils/connection.js";
import FC from "../fc";
const firmware_flasher = { const firmware_flasher = {
targets: null, targets: null,
@ -255,6 +256,9 @@ firmware_flasher.initialize = function (callback) {
} }
// extract osd protocols from general options and add to osdProtocols // extract osd protocols from general options and add to osdProtocols
console.log(`${self.logHead} buildOptions`, FC.CONFIG.buildOptions);
self.cloudBuildOptions = FC.CONFIG.buildOptions || [];
console.log(`${self.logHead} generalOptions`, self.cloudBuildOptions);
data.osdProtocols = data.generalOptions data.osdProtocols = data.generalOptions
.filter((option) => option.group === "OSD") .filter((option) => option.group === "OSD")
.map((option) => { .map((option) => {