This commit is contained in:
2026-05-26 17:19:45 +02:00
parent 87cba0b419
commit 2d3ea34603
12 changed files with 239 additions and 17 deletions

View File

@@ -1,6 +1,6 @@
### Logging ### Logging
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y #CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_DEBUG=y CONFIG_DEBUG=y
CONFIG_DEBUG_OPTIMIZATIONS=y CONFIG_DEBUG_OPTIMIZATIONS=y
@@ -14,15 +14,15 @@ CONFIG_BATT_MGMT_LOG_LEVEL_DBG=y
CONFIG_USB_MGMT_LOG_LEVEL_DBG=y CONFIG_USB_MGMT_LOG_LEVEL_DBG=y
### Bluetooth ### Bluetooth
CONFIG_BLE_MGMT=n CONFIG_BLE_MGMT=y
CONFIG_BT_LOG_LEVEL_WRN=n CONFIG_BT_LOG_LEVEL_WRN=y
### Audio ### Audio
CONFIG_BUZZ_AUDIO=n CONFIG_BUZZ_AUDIO=n
### Shell features shared by all debug variants ### Shell features shared by all debug variants
CONFIG_SHELL=y CONFIG_SHELL=y
CONFIG_SHELL_LOG_BACKEND=n CONFIG_SHELL_LOG_BACKEND=y
CONFIG_FILE_SYSTEM_SHELL=y CONFIG_FILE_SYSTEM_SHELL=y
CONFIG_SHELL_STACK_SIZE=2048 CONFIG_SHELL_STACK_SIZE=2048
CONFIG_FILE_SYSTEM_SHELL_LS_SIZE=y CONFIG_FILE_SYSTEM_SHELL_LS_SIZE=y

View File

@@ -8,11 +8,11 @@
#define BATT_MGMT_OVERSAMPLING_16X 16U #define BATT_MGMT_OVERSAMPLING_16X 16U
typedef enum { typedef enum {
BATT_STATE_DISCHARGING = 0, BATT_STATE_DISCHARGING = 0x00,
BATT_STATE_FULL, BATT_STATE_FULL= 0x01,
BATT_STATE_CHARGING, BATT_STATE_CHARGING = 0x02,
BATT_STATE_ERROR, BATT_STATE_ERROR = 0x03,
BATT_STATE_UNKNOWN, BATT_STATE_UNKNOWN = 0x04,
} batt_mgmt_state_t; } batt_mgmt_state_t;
typedef struct { typedef struct {

View File

@@ -36,6 +36,7 @@ enum buzz_data_type
BUZZ_DATA_DEVICE_INFO = 0x02, BUZZ_DATA_DEVICE_INFO = 0x02,
BUZZ_DATA_FS_INFO = 0x03, BUZZ_DATA_FS_INFO = 0x03,
BUZZ_DATA_FW_INFO = 0x04, BUZZ_DATA_FW_INFO = 0x04,
BUZZ_DATA_BATT_INFO = 0x05,
BUZZ_DATA_FILE_GET = 0x20, BUZZ_DATA_FILE_GET = 0x20,
BUZZ_DATA_FILE_PUT = 0x21, BUZZ_DATA_FILE_PUT = 0x21,
@@ -122,6 +123,17 @@ struct __attribute__((packed)) buzz_resp_fw_info
uint8_t kernel_version_length; /* Länge der Kernel-Versionszeichenkette */ uint8_t kernel_version_length; /* Länge der Kernel-Versionszeichenkette */
char data[]; /* Variabler String ohne Null-Terminierung: [fw_version][kernel_version] */ char data[]; /* Variabler String ohne Null-Terminierung: [fw_version][kernel_version] */
}; };
/* Payload für die Batterie-Infos */
struct __attribute__((packed)) buzz_resp_batt_info
{
uint8_t data_type; /* BUZZ_DATA_BATT_INFO */
uint8_t batt_status; /* batt_mgmt_state_t */
uint8_t batt_level; /* 0..4 */
uint8_t batt_percent; /* 0..100 */
uint16_t batt_voltage_mv; /* Little Endian */
};
/* Payload für das Entfernen einer Datei */ /* Payload für das Entfernen einer Datei */
struct __attribute__((packed)) buzz_rm_file_payload struct __attribute__((packed)) buzz_rm_file_payload
{ {

View File

@@ -7,6 +7,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "buzz_proto.h" #include "buzz_proto.h"
#include "batt_mgmt.h"
#include "fs_mgmt.h" #include "fs_mgmt.h"
#include "fw_mgmt.h" #include "fw_mgmt.h"
@@ -293,6 +294,41 @@ static void handle_fw_info_request(struct buzz_frame_msg *msg)
} }
} }
static void handle_batt_info_request(struct buzz_frame_msg *msg)
{
struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr;
struct buzz_resp_batt_info *resp_data = (struct buzz_resp_batt_info *)(msg->data_ptr + sizeof(*hdr));
batt_mgmt_info_t batt_info;
uint16_t voltage_mv = 0;
int rc = batt_mgmt_get_info(&batt_info);
if (rc < 0)
{
LOG_WRN("Failed to get battery info: %d", rc);
send_error_frame(msg, abs(rc));
return;
}
if (batt_info.voltage_mv > 0)
{
voltage_mv = (batt_info.voltage_mv > UINT16_MAX) ? UINT16_MAX : (uint16_t)batt_info.voltage_mv;
}
hdr->frame_type = BUZZ_FRAME_RESPONSE;
hdr->payload_length = sys_cpu_to_le16(sizeof(struct buzz_resp_batt_info));
resp_data->data_type = BUZZ_DATA_BATT_INFO;
resp_data->batt_status = (uint8_t)batt_info.state;
resp_data->batt_level = batt_info.level;
resp_data->batt_percent = batt_info.percent;
resp_data->batt_voltage_mv = sys_cpu_to_le16(voltage_mv);
if (msg->reply_cb)
{
msg->reply_cb(msg->data_ptr, sizeof(struct buzz_proto_header) + sizeof(struct buzz_resp_batt_info));
}
}
static void handle_ls_request(struct buzz_frame_msg *msg) static void handle_ls_request(struct buzz_frame_msg *msg)
{ {
struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr; struct buzz_proto_header *hdr = (struct buzz_proto_header *)msg->data_ptr;
@@ -698,6 +734,11 @@ static void handle_request(struct buzz_frame_msg *msg)
handle_fw_info_request(msg); handle_fw_info_request(msg);
break; break;
case BUZZ_DATA_BATT_INFO:
LOG_DBG("Received BATT Info Request");
handle_batt_info_request(msg);
break;
case BUZZ_DATA_FILE_GET: case BUZZ_DATA_FILE_GET:
LOG_DBG("Received FILE_GET Request"); LOG_DBG("Received FILE_GET Request");
handle_file_get_request(msg, false); handle_file_get_request(msg, false);

View File

@@ -93,9 +93,23 @@ int main(void)
} else { } else {
LOG_WRN("Battery info read failed: %d", batt_rc); LOG_WRN("Battery info read failed: %d", batt_rc);
} }
for (;;) {
k_sleep(K_SECONDS(5));
batt_rc = batt_mgmt_get_info(&batt_info);
if (batt_rc == 0) {
LOG_INF("Battery: %d mV, %u%%, level=%u, state=%s (%d)",
batt_info.voltage_mv,
batt_info.percent,
batt_info.level,
battery_state_to_str(batt_info.state),
batt_info.state);
} else {
LOG_WRN("Battery info read failed: %d", batt_rc);
}
}
#endif // CONFIG_BATT_MGMT #endif // CONFIG_BATT_MGMT
#endif // CONFIG_LOG #endif // CONFIG_LOG
for (;;) { for (;;) {
int32_t rem_ms = k_sleep(K_FOREVER); int32_t rem_ms = k_sleep(K_FOREVER);
LOG_WRN("main woke unexpectedly (remaining=%d ms)", rem_ms); LOG_WRN("main woke unexpectedly (remaining=%d ms)", rem_ms);

View File

@@ -92,6 +92,7 @@ Das Protokoll ist so ausgelegt, dass es mit mindestens 100 Bytes auskommen sollt
| `0x02` | `DEVICE_INFO` | aktiv | Device-Infos (Board, Revision, SOC, ID) | | `0x02` | `DEVICE_INFO` | aktiv | Device-Infos (Board, Revision, SOC, ID) |
| `0x03` | `FS_INFO` | aktiv | Dateisystem- und Pfadinfos | | `0x03` | `FS_INFO` | aktiv | Dateisystem- und Pfadinfos |
| `0x04` | `FW_INFO` | aktiv | Info über Firmware-Status und -Version sowie Kernelversion | | `0x04` | `FW_INFO` | aktiv | Info über Firmware-Status und -Version sowie Kernelversion |
| `0x05` | `BATT_INFO` | aktiv | Info über die Batterie |
| `0x20` | `FILE_GET` | aktiv | Datei vom Device streamen | | `0x20` | `FILE_GET` | aktiv | Datei vom Device streamen |
| `0x21` | `FILE_PUT` | aktiv | Datei zum Device hochladen | | `0x21` | `FILE_PUT` | aktiv | Datei zum Device hochladen |
| `0x22` | `TAGS_GET` | aktiv | nur Tag-Bereich streamen | | `0x22` | `TAGS_GET` | aktiv | nur Tag-Bereich streamen |
@@ -238,6 +239,7 @@ Request: keine Zusatzdaten
Response: Response:
```c ```c
uint8_t data_type; /* 0x04 */
uint8_t fw_status; /* 0x00: Confirmed, 0x01: Pending, 0x02: Testing, 0xFF: Unbekannt */ uint8_t fw_status; /* 0x00: Confirmed, 0x01: Pending, 0x02: Testing, 0xFF: Unbekannt */
uint32_t slot1_size; /* (LE) Grösse des Firmware Update Slots */ uint32_t slot1_size; /* (LE) Grösse des Firmware Update Slots */
uint8_t fw_version_len; /* Länge des Firmware-Versionsstring */ uint8_t fw_version_len; /* Länge des Firmware-Versionsstring */
@@ -247,6 +249,20 @@ uint8_t data[]; /* FW-Version und Kernelversion, ohne Nullterminier
***Hinweis:*** in der Aktuellen implementierung werden die Versionen auf 32 Zeichen limitiert. ***Hinweis:*** in der Aktuellen implementierung werden die Versionen auf 32 Zeichen limitiert.
### `BATT_INFO` (`0x05`)
Request: keine Zusatzdaten
Response:
```c
uint8_t data_type; /* 0x05 */
uint8_t batt_status; /* 0x00: Discharging, 0x01: Full, 0x02: Charging, 0x03: Error, 0x04: Unknown */
uint8_t batt_level; /* 0-4, Anzahl Striche für den Akku */
uint8_t batt_percent; /* Akku-Füllstand in Prozent */
uint16_t batt_voltage_mv; /* (LE) Batteriespannung in mV */
```
### `LS` (`0x40`) ### `LS` (`0x40`)
Request-Payload: Request-Payload:

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import FlashUsage from "./FlashUsage.svelte"; import FlashUsage from "./FlashUsage.svelte";
import { deviceInfo, fwInfo } from "../lib/store"; import { battInfo, deviceInfo, fwInfo } from "../lib/store";
import { FW_STATUS } from "../lib/protocol/constants"; import { BATT_STATUS, FW_STATUS } from "../lib/protocol/constants";
import { tooltip } from "../lib/actions/tooltip"; import { tooltip } from "../lib/actions/tooltip";
import { import {
CheckCircleIcon, CheckCircleIcon,
@@ -13,6 +13,52 @@
BatteryFullIcon, BatteryFullIcon,
BatteryChargingIcon, BatteryChargingIcon,
} from "phosphor-svelte"; } from "phosphor-svelte";
function clampBatteryLevel(level: number): number {
if (Number.isNaN(level)) return 0;
return Math.max(0, Math.min(4, Math.trunc(level)));
}
$: resolvedBattIcon = (() => {
if (!$battInfo) return BatteryEmptyIcon;
if ($battInfo.battStatus === BATT_STATUS.CHARGING) return BatteryChargingIcon;
switch (clampBatteryLevel($battInfo.battLevel)) {
case 0:
return BatteryEmptyIcon;
case 1:
return BatteryLowIcon;
case 2:
return BatteryMediumIcon;
case 3:
return BatteryHighIcon;
default:
return BatteryFullIcon;
}
})();
$: battStatusText = (() => {
if (!$battInfo) return "unbekannt";
switch ($battInfo.battStatus) {
case BATT_STATUS.DISCHARGING:
return "Entladen";
case BATT_STATUS.FULL:
return "Voll";
case BATT_STATUS.CHARGING:
return "Laden";
case BATT_STATUS.ERROR:
return "Fehler";
default:
return "Unbekannt";
}
})();
$: battIconClass =
$battInfo?.battStatus === BATT_STATUS.ERROR
? "w-5 h-5 text-red-500"
: $battInfo?.battStatus === BATT_STATUS.CHARGING
? "w-5 h-5 text-emerald-600"
: "w-5 h-5";
</script> </script>
<div class="text-sm"> <div class="text-sm">
@@ -127,7 +173,14 @@
<tr> <tr>
<td class="key">Batterie</td> <td class="key">Batterie</td>
<td class="value flex items-center gap-2"> <td class="value flex items-center gap-2">
85% <BatteryChargingIcon weight="bold" class="w-5 h-5" /> 1200mAh {#if $battInfo}
<span>{$battInfo.battPercent}%</span>
<svelte:component this={resolvedBattIcon} weight="bold" class={battIconClass} />
<span class="text-text-muted">{$battInfo.battVoltageMv} mV</span>
<span class="text-text-muted">({battStatusText})</span>
{:else}
unbekannt
{/if}
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@@ -27,6 +27,7 @@ export const DATA = {
DEVICE_INFO: 0x02, DEVICE_INFO: 0x02,
FS_INFO: 0x03, FS_INFO: 0x03,
FW_INFO: 0x04, FW_INFO: 0x04,
BATT_INFO: 0x05,
FILE_GET: 0x20, FILE_GET: 0x20,
FILE_PUT: 0x21, FILE_PUT: 0x21,
@@ -65,4 +66,12 @@ export const FW_STATUS = {
PENDING: 0x01, PENDING: 0x01,
TESTING: 0x02, TESTING: 0x02,
UNKNOWN: 0xFF, UNKNOWN: 0xFF,
} }
export const BATT_STATUS = {
DISCHARGING: 0x00,
FULL: 0x01,
CHARGING: 0x02,
ERROR: 0x03,
UNKNOWN: 0x04,
};

View File

@@ -1,5 +1,5 @@
import { FRAME, DATA, ZEPHYR_ERRORS } from './constants'; import { FRAME, DATA, ZEPHYR_ERRORS } from './constants';
import { protocolInfo, deviceInfo, fsInfo, transferStats, fwInfo, resetTransferStats, transferDetails } from '../store'; import { protocolInfo, deviceInfo, fsInfo, transferStats, fwInfo, battInfo, resetTransferStats, transferDetails } from '../store';
import { addToast } from '../toast'; import { addToast } from '../toast';
import { SETTINGS } from '../settings'; import { SETTINGS } from '../settings';
import { crc32 } from './crc32'; import { crc32 } from './crc32';
@@ -84,6 +84,18 @@ export function parseIncomingFrame(view: DataView, sender: FrameSender) {
const fwVersion = new TextDecoder().decode(new Uint8Array(view.buffer, 11, fw_version_length)); const fwVersion = new TextDecoder().decode(new Uint8Array(view.buffer, 11, fw_version_length));
const kernelVersion = new TextDecoder().decode(new Uint8Array(view.buffer, 11 + fw_version_length, kernel_version_length)); const kernelVersion = new TextDecoder().decode(new Uint8Array(view.buffer, 11 + fw_version_length, kernel_version_length));
fwInfo.set({ fwStatus, slot1Size, fwVersion, kernelVersion }); fwInfo.set({ fwStatus, slot1Size, fwVersion, kernelVersion });
break;
case DATA.BATT_INFO:
if (payloadLength < 6) {
console.warn(`Invalid BATT_INFO payload length: ${payloadLength}`);
break;
}
const battStatus = view.getUint8(4);
const battLevel = view.getUint8(5);
const battPercent = view.getUint8(6);
const battVoltageMv = view.getUint16(7, true);
battInfo.set({ battStatus, battLevel, battPercent, battVoltageMv });
break;
} }
break; break;
@@ -365,6 +377,17 @@ export function buildFWInfoRequest(): ArrayBuffer {
return buffer; return buffer;
} }
export function buildBattInfoRequest(): ArrayBuffer {
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setUint8(0, FRAME.REQUEST);
view.setUint16(1, 1, true);
view.setUint8(3, DATA.BATT_INFO);
return buffer;
}
export function buildLSRequest(path: string): ArrayBuffer { export function buildLSRequest(path: string): ArrayBuffer {
const encoder = new TextEncoder(); const encoder = new TextEncoder();
const pathBytes = encoder.encode(path); const pathBytes = encoder.encode(path);

View File

@@ -53,6 +53,13 @@ export interface FwInfo {
kernelVersion: string; kernelVersion: string;
} }
export interface BattInfo {
battStatus: number;
battLevel: number;
battPercent: number;
battVoltageMv: number;
}
export interface StorageUsage { export interface StorageUsage {
totalBytes: number; totalBytes: number;
freeBytes: number; freeBytes: number;
@@ -84,6 +91,7 @@ export const protocolInfo = writable<ProtocolInfo | null>(null);
export const deviceInfo = writable<DeviceInfo | null>(null); export const deviceInfo = writable<DeviceInfo | null>(null);
export const fsInfo = writable<FsInfo | null>(null); export const fsInfo = writable<FsInfo | null>(null);
export const fwInfo = writable<FwInfo | null>(null); export const fwInfo = writable<FwInfo | null>(null);
export const battInfo = writable<BattInfo | null>(null);
// Dateilisten // Dateilisten
export const buzzerAudioFiles = writable<BuzzerFile[]>([]); export const buzzerAudioFiles = writable<BuzzerFile[]>([]);
@@ -277,6 +285,7 @@ export function resetRemote(): void {
deviceInfo.set(null); deviceInfo.set(null);
fsInfo.set(null); fsInfo.set(null);
fwInfo.set(null); fwInfo.set(null);
battInfo.set(null);
activeDeviceId.set(null); activeDeviceId.set(null);
buzzerAudioFiles.set([]); buzzerAudioFiles.set([]);
buzzerSysFiles.set([]); buzzerSysFiles.set([]);

View File

@@ -1,6 +1,6 @@
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { isConnected, deviceInfo, fsInfo, fwInfo, buzzerAudioFiles, buzzerSysFiles, isTransferingRemote, isFetchingLocal, storageUsage, localAudioFiles, transferStats } from './store'; import { isConnected, deviceInfo, fsInfo, fwInfo, buzzerAudioFiles, buzzerSysFiles, isTransferingRemote, isFetchingLocal, storageUsage, localAudioFiles, transferStats } from './store';
import { requestProtocolInfo, requestFSInfo, fetchDirectory, getFile, putFile, deleteRemoteFile, requestDeviceInfo, requestFWInfo } from './transport'; import { requestProtocolInfo, requestFSInfo, fetchDirectory, getFile, putFile, deleteRemoteFile, requestDeviceInfo, requestFWInfo, requestBattInfo } from './transport';
import type { BuzzerFile } from './types'; import type { BuzzerFile } from './types';
import { addToast } from './toast'; import { addToast } from './toast';
import { getLocalFiles, deleteLocalFile, getLocalFile } from './db'; import { getLocalFiles, deleteLocalFile, getLocalFile } from './db';
@@ -28,6 +28,7 @@ export async function refreshRemote() {
await requestProtocolInfo(); await requestProtocolInfo();
await requestFSInfo(); await requestFSInfo();
await requestFWInfo(); await requestFWInfo();
await requestBattInfo();
await requestDeviceInfo(); await requestDeviceInfo();
// Kurze Verzögerung für Store-Propagation // Kurze Verzögerung für Store-Propagation

View File

@@ -1,4 +1,4 @@
import { buildLSRequest, buildProtocolInfoRequest, buildDeviceInfoRequest, buildFSInfoRequest, buildFWInfoRequest, setLsResolver, buildFileGetRequest, setFileGetResolver, buildTagsGetRequest, uploadState } from './protocol/parser'; import { buildLSRequest, buildProtocolInfoRequest, buildDeviceInfoRequest, buildFSInfoRequest, buildFWInfoRequest, buildBattInfoRequest, setLsResolver, buildFileGetRequest, setFileGetResolver, buildTagsGetRequest, uploadState } from './protocol/parser';
import { crc32 } from './protocol/crc32'; import { crc32 } from './protocol/crc32';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { protocolInfo, transferStats, } from './store'; import { protocolInfo, transferStats, } from './store';
@@ -10,9 +10,45 @@ const isMac = navigator.userAgent.includes('Macintosh') || navigator.userAgent.i
const MAX_INFLIGHT = isMac ? SETTINGS.bluetooth.appleMaxInflight : Infinity; // iOS erlaubt nur wenige unbestätigte Nachrichten const MAX_INFLIGHT = isMac ? SETTINGS.bluetooth.appleMaxInflight : Infinity; // iOS erlaubt nur wenige unbestätigte Nachrichten
console.log("Transport: Max Inflight Frames =", MAX_INFLIGHT); console.log("Transport: Max Inflight Frames =", MAX_INFLIGHT);
const BATT_POLL_INTERVAL_MS = 60_000;
export type FrameSender = (buffer: ArrayBuffer) => Promise<void>; export type FrameSender = (buffer: ArrayBuffer) => Promise<void>;
let currentSender: FrameSender | null = null; let currentSender: FrameSender | null = null;
let battPollTimer: ReturnType<typeof setInterval> | null = null;
let isBattPollInFlight = false;
function stopBattPolling() {
if (battPollTimer) {
clearInterval(battPollTimer);
battPollTimer = null;
}
}
function shouldSkipBattPoll(): boolean {
return isListing || isFileTransferring || uploadState.active;
}
async function pollBatteryInfo() {
if (!currentSender || isBattPollInFlight || shouldSkipBattPoll()) {
return;
}
isBattPollInFlight = true;
try {
await requestBattInfo();
} catch (error) {
console.debug("Periodic BATT_INFO request failed:", error);
} finally {
isBattPollInFlight = false;
}
}
function startBattPolling() {
stopBattPolling();
battPollTimer = setInterval(() => {
void pollBatteryInfo();
}, BATT_POLL_INTERVAL_MS);
}
export function registerTransport(sender: FrameSender | null) { export function registerTransport(sender: FrameSender | null) {
currentSender = sender; currentSender = sender;
@@ -21,6 +57,7 @@ export function registerTransport(sender: FrameSender | null) {
// NEU: Wird von bluetooth.ts oder serial.ts nach dem physischen Connect gerufen // NEU: Wird von bluetooth.ts oder serial.ts nach dem physischen Connect gerufen
export async function handleTransportConnect(sender: FrameSender) { export async function handleTransportConnect(sender: FrameSender) {
registerTransport(sender); registerTransport(sender);
stopBattPolling();
try { try {
// Basis-Informationen zwingend vorab laden // Basis-Informationen zwingend vorab laden
@@ -28,9 +65,11 @@ export async function handleTransportConnect(sender: FrameSender) {
await requestFSInfo(); await requestFSInfo();
await requestDeviceInfo(); await requestDeviceInfo();
await requestFWInfo(); await requestFWInfo();
await requestBattInfo();
// Erst wenn diese Basisdaten da sind, wird die UI freigeschaltet // Erst wenn diese Basisdaten da sind, wird die UI freigeschaltet
isConnected.set(true); isConnected.set(true);
startBattPolling();
} catch (error) { } catch (error) {
console.error("Transport-Initialisierung fehlgeschlagen:", error); console.error("Transport-Initialisierung fehlgeschlagen:", error);
handleTransportDisconnect(); handleTransportDisconnect();
@@ -58,6 +97,10 @@ export async function requestFWInfo() {
await sendFrame(buildFWInfoRequest()); await sendFrame(buildFWInfoRequest());
} }
export async function requestBattInfo() {
await sendFrame(buildBattInfoRequest());
}
let isListing = false; let isListing = false;
export async function fetchDirectory(path: string): Promise<any[]> { export async function fetchDirectory(path: string): Promise<any[]> {
@@ -85,6 +128,7 @@ export async function fetchDirectory(path: string): Promise<any[]> {
} }
export function handleTransportDisconnect() { export function handleTransportDisconnect() {
stopBattPolling();
registerTransport(null); registerTransport(null);
resetRemote(); resetRemote();
} }