Skip to content

Commit cc60524

Browse files
Suggest closing other processes/tabs when Web USB gets "Unable to claim interface" error (#193)
1 parent fea3744 commit cc60524

File tree

3 files changed

+38
-32
lines changed

3 files changed

+38
-32
lines changed

src/components/connection-prompt/ConnectDialogContainer.svelte

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import BluetoothConnectingDialog from './bluetooth/BluetoothConnectingDialog.svelte';
3030
import SelectMicrobitDialogBluetooth from './bluetooth/SelectMicrobitDialogBluetooth.svelte';
3131
import MicrobitWearingInstructionDialog from './MicrobitWearingInstructionDialog.svelte';
32-
import WebUsbTryAgain from './WebUsbTryAgain.svelte';
32+
import WebUsbTryAgain, { USBTryAgainType } from './WebUsbTryAgain.svelte';
3333
import { onDestroy, onMount } from 'svelte';
3434
import { get, Unsubscriber } from 'svelte/store';
3535
import { compatibility } from '../../script/stores/uiStore';
@@ -40,7 +40,7 @@
4040
const { bluetooth, usb } = get(compatibility);
4141
let endOfFlow = false;
4242
let flashStage: FlashStage = usb ? 'bluetooth' : 'radio-sender';
43-
let reconnectRequired = false;
43+
let usbTryAgainType: USBTryAgainType = 'replug microbit';
4444
let flashProgress = 0;
4545
4646
const stageToHex = (flashStage: FlashStage): HexType => {
@@ -72,30 +72,38 @@
7272
break;
7373
} else if (/No device selected/.test(err.message)) {
7474
$connectionDialogState.connectionState = ConnectDialogStates.USB_TRY_AGAIN;
75-
75+
usbTryAgainType = 'select microbit';
76+
break;
77+
} else if (/Unable to claim interface/.test(err.message)) {
78+
$connectionDialogState.connectionState = ConnectDialogStates.USB_TRY_AGAIN;
79+
usbTryAgainType = 'close tabs';
7680
break;
7781
} else {
7882
// Unhandled error. User will need to reconnect their micro:bit
7983
$connectionDialogState.connectionState = ConnectDialogStates.USB_TRY_AGAIN;
80-
reconnectRequired = true;
84+
usbTryAgainType = 'replug microbit';
8185
break;
8286
}
8387
default: {
8488
$connectionDialogState.connectionState = ConnectDialogStates.USB_TRY_AGAIN;
85-
reconnectRequired = true;
89+
usbTryAgainType = 'replug microbit';
8690
}
8791
}
8892
};
8993
94+
const handleConnectionError = (err: any) => {
95+
if (flashStage === 'bluetooth') {
96+
$connectionDialogState.connectionState = ConnectDialogStates.MANUAL_TUTORIAL;
97+
} else {
98+
handleWebUSBError(err);
99+
}
100+
};
101+
90102
async function tryMicrobitConnection(): Promise<void> {
91103
try {
92104
await Microbits.linkMicrobit();
93105
} catch (err) {
94-
if (flashStage === 'bluetooth') {
95-
$connectionDialogState.connectionState = ConnectDialogStates.MANUAL_TUTORIAL;
96-
} else {
97-
$connectionDialogState.connectionState = ConnectDialogStates.USB_TRY_AGAIN;
98-
}
106+
handleConnectionError(err);
99107
return;
100108
}
101109
const friendlyName = await getMicrobitName();
@@ -116,11 +124,7 @@
116124
}
117125
return friendlyName;
118126
} catch (err) {
119-
if (flashStage === 'bluetooth') {
120-
$connectionDialogState.connectionState = ConnectDialogStates.MANUAL_TUTORIAL;
121-
} else {
122-
handleWebUSBError(err);
123-
}
127+
handleConnectionError(err);
124128
}
125129
}
126130
@@ -144,11 +148,7 @@
144148
onConnectingSerial(friendlyName);
145149
}
146150
} catch (err) {
147-
if (flashStage === 'bluetooth') {
148-
$connectionDialogState.connectionState = ConnectDialogStates.MANUAL_TUTORIAL;
149-
} else {
150-
handleWebUSBError(err);
151-
}
151+
handleConnectionError(err);
152152
}
153153
}
154154
@@ -169,7 +169,6 @@
169169
setTimeout(() => {
170170
$connectionDialogState.connectionState = ConnectDialogStates.NONE;
171171
endOfFlow = false;
172-
reconnectRequired = false;
173172
}, 200);
174173
}
175174
@@ -381,11 +380,10 @@
381380
ConnectDialogStates.CONNECT_BATTERY)} />
382381
{:else if $connectionDialogState.connectionState === ConnectDialogStates.USB_TRY_AGAIN}
383382
<WebUsbTryAgain
384-
{reconnectRequired}
383+
type={usbTryAgainType}
385384
onCancel={endFlow}
386385
onTryAgain={() => {
387386
$connectionDialogState.connectionState = ConnectDialogStates.CONNECT_CABLE;
388-
reconnectRequired = false;
389387
}} />
390388
{/if}
391389
</StandardDialog>

src/components/connection-prompt/WebUsbTryAgain.svelte

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,31 @@
44
SPDX-License-Identifier: MIT
55
-->
66

7+
<script lang="ts" context="module">
8+
export type USBTryAgainType = 'replug microbit' | 'select microbit' | 'close tabs';
9+
</script>
10+
711
<script lang="ts">
812
import { t } from '../../i18n';
913
import DialogHeading from '../DialogHeading.svelte';
1014
import StandardButton from '../StandardButton.svelte';
1115
1216
export let onTryAgain: () => void;
1317
export let onCancel: () => void;
14-
export let reconnectRequired: boolean = false;
18+
export let type: USBTryAgainType = 'replug microbit';
1519
</script>
1620

1721
<div class="w-175">
1822
<DialogHeading>{$t('connectMB.usbTryAgain.heading')}</DialogHeading>
1923
<div class="space-y-5">
20-
{#if reconnectRequired}
21-
<p>{$t('connectMB.usb.error1')}</p>
22-
<p>{$t('connectMB.usb.error2')}</p>
24+
{#if type === 'select microbit'}
25+
<p>{$t('connectMB.usbTryAgain.selectMicrobit')}</p>
26+
{:else if type === 'close tabs'}
27+
<p>{$t('connectMB.usbTryAgain.closeTabs1')}</p>
28+
<p>{$t('connectMB.usbTryAgain.closeTabs2')}</p>
2329
{:else}
24-
<p>{$t('connectMB.usbTryAgain.subtitle')}</p>
30+
<p>{$t('connectMB.usbTryAgain.replugMicrobit1')}</p>
31+
<p>{$t('connectMB.usbTryAgain.replugMicrobit2')}</p>
2532
{/if}
2633
<div class="flex justify-end gap-x-5">
2734
<StandardButton onClick={onCancel}>{$t('actions.cancel')}</StandardButton>

src/messages/ui.en.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,11 @@
131131
"connectMB.tryAgain": "Try again",
132132

133133
"connectMB.usbTryAgain.heading": "Connect using WebUSB",
134-
"connectMB.usbTryAgain.subtitle": "You didn't select a micro:bit. Do you want to try again?",
135-
136-
"connectMB.usb.error1": "A WebUSB error occured.",
137-
"connectMB.usb.error2": "Please unplug and replug the micro:bit before trying again.",
134+
"connectMB.usbTryAgain.selectMicrobit": "You didn't select a micro:bit. Do you want to try again?",
135+
"connectMB.usbTryAgain.closeTabs1": "Another process is connected to this device.",
136+
"connectMB.usbTryAgain.closeTabs2": "Close any other tabs that may be using WebUSB (e.g. MakeCode, Python Editor, machine learning tool), or unplug and replug the micro:bit before trying again.",
137+
"connectMB.usbTryAgain.replugMicrobit1": "A WebUSB error occurred.",
138+
"connectMB.usbTryAgain.replugMicrobit2": "Please unplug and replug the micro:bit before trying again.",
138139

139140
"connectMB.radioStart.heading": "What you will need to get started:",
140141
"connectMB.radioStart.requirements1": "2 micro:bits",

0 commit comments

Comments
 (0)