Skip to content

Commit 48c4bed

Browse files
Alberto Iannacconefstasi
Alberto Iannaccone
authored andcommitted
list available firmware in update-frimware-dialog
1 parent 12e62ce commit 48c4bed

File tree

2 files changed

+287
-12
lines changed

2 files changed

+287
-12
lines changed

arduino-ide-extension/src/browser/dialogs/firmware-uploader/firmware-uploader-dialog.tsx

Lines changed: 278 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,296 @@ import { AbstractDialog, DialogProps } from '@theia/core/lib/browser/dialogs';
44
import { Widget } from '@phosphor/widgets';
55
import { Message } from '@phosphor/messaging';
66
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
7-
import { BoardsServiceProvider } from '../../boards/boards-service-provider';
8-
import { FirmwareUploaderComponent } from './firmware-uploader-component';
7+
import {
8+
AvailableBoard,
9+
BoardsServiceProvider,
10+
} from '../../boards/boards-service-provider';
11+
import { ArduinoSelect } from '../../widgets/arduino-select';
12+
import {
13+
ArduinoFirmwareUploader,
14+
FirmwareInfo,
15+
} from '../../../common/protocol/arduino-firmware-uploader';
16+
17+
type BoardOptionValue = {
18+
port: string;
19+
fqbn: string;
20+
};
21+
type SelectBoardOption = {
22+
label: string;
23+
value: BoardOptionValue;
24+
} | null;
25+
26+
export const FirmwareUploaderComponent = ({
27+
boardsServiceClient,
28+
fwuploader,
29+
}: {
30+
boardsServiceClient: BoardsServiceProvider;
31+
fwuploader: ArduinoFirmwareUploader;
32+
}): React.ReactElement => {
33+
// boolean states for buttons
34+
const [boardsFetching, setBoardsFetching] = React.useState(false);
35+
const [firmwaresFetching, setFirmwaresFetching] = React.useState(false);
36+
const [installingFw, setInstallingFw] = React.useState(false);
37+
const [installFeedback, setInstallFeedback] = React.useState<
38+
'ok' | 'fail' | null
39+
>(null);
40+
41+
const [selectedBoard, setSelectedBoard] =
42+
React.useState<SelectBoardOption>(null);
43+
44+
const [selectBoardPlaceholder, setSelectBoardPlaceholder] =
45+
React.useState('');
46+
const [availableBoards, setAvailableBoards] = React.useState<
47+
SelectBoardOption[]
48+
>([]);
49+
50+
const [selectedFirmware, setSelectedFirmware] = React.useState<{
51+
label: string;
52+
value: FirmwareInfo;
53+
} | null>(null);
54+
const [availableFirmwares, setAvailableFirmwares] = React.useState<
55+
{
56+
label: string;
57+
value: FirmwareInfo;
58+
}[]
59+
>([]);
60+
61+
React.useEffect(() => {
62+
boardsServiceClient.onAvailableBoardsChanged((availableBoards) => {
63+
setBoardsFetching(true);
64+
let placeholderTxt = 'Select a board...';
65+
let selectedBoard = -1;
66+
const boardsList = availableBoards
67+
.filter(
68+
(board) =>
69+
!!board.fqbn && board.state === AvailableBoard.State.recognized
70+
)
71+
.map((board: AvailableBoard & { fqbn: string }, i) => {
72+
if (board.selected) {
73+
selectedBoard = i;
74+
}
75+
return {
76+
label: `${board.name} at ${board.port?.address}`,
77+
value: { port: board.port?.address || '', fqbn: board.fqbn },
78+
};
79+
});
80+
81+
if (boardsList.length === 0) {
82+
placeholderTxt = 'No board connected to serial port';
83+
}
84+
85+
setSelectBoardPlaceholder(placeholderTxt);
86+
setAvailableBoards(boardsList);
87+
setSelectedBoard(boardsList[selectedBoard] || null);
88+
setBoardsFetching(false);
89+
});
90+
}, [boardsServiceClient]);
91+
92+
const fetchFirmwares = React.useCallback(async () => {
93+
setInstallFeedback(null);
94+
setFirmwaresFetching(true);
95+
if (!selectedBoard) {
96+
return;
97+
}
98+
99+
// fetch the firmwares for the selected board
100+
const firmwares = (
101+
await fwuploader.availableFirmwares(selectedBoard.value.fqbn)
102+
).map((f) => ({
103+
label: f.firmware_version,
104+
value: f,
105+
}));
106+
107+
setAvailableFirmwares(firmwares);
108+
if (firmwares.length > 0) setSelectedFirmware(firmwares[0]);
109+
setFirmwaresFetching(false);
110+
}, [fwuploader, selectedBoard]);
111+
112+
const installFirmware = React.useCallback(async () => {
113+
setInstallFeedback(null);
114+
setInstallingFw(true);
115+
116+
const installStatus =
117+
!!selectedFirmware &&
118+
!!selectedBoard?.value.port &&
119+
(await fwuploader.flash(
120+
selectedFirmware.value,
121+
selectedBoard?.value.port
122+
));
123+
124+
setInstallFeedback((installStatus && 'ok') || 'fail');
125+
setInstallingFw(false);
126+
}, [fwuploader, selectedBoard, selectedFirmware]);
127+
128+
return (
129+
<div id="widget-container">
130+
<label htmlFor="board-select">Select board</label>
131+
<div className="row">
132+
<div className="fl1">
133+
<ArduinoSelect
134+
id="board-select"
135+
isDisabled={availableBoards.length === 0 || boardsFetching}
136+
placeholder={selectBoardPlaceholder}
137+
options={availableBoards}
138+
value={selectedBoard}
139+
tabSelectsValue={false}
140+
onChange={(value) => {
141+
if (value) {
142+
// clear previously available firmwares for the board
143+
setInstallFeedback(null);
144+
setAvailableFirmwares([]);
145+
setSelectedBoard(value);
146+
}
147+
}}
148+
/>
149+
</div>
150+
<button
151+
type="button"
152+
className="theia-button secondary"
153+
disabled={
154+
selectedBoard === null || firmwaresFetching || boardsFetching
155+
}
156+
onClick={fetchFirmwares}
157+
>
158+
Check Updates
159+
</button>
160+
</div>
161+
{availableFirmwares.length > 0 && (
162+
<>
163+
<div className="row">
164+
<label htmlFor="firmware-select" className="fl1">
165+
Select firmware version
166+
</label>
167+
<ArduinoSelect
168+
id="firmware-select"
169+
isDisabled={
170+
availableBoards.length === 0 ||
171+
firmwaresFetching ||
172+
installingFw
173+
}
174+
options={availableFirmwares}
175+
value={selectedFirmware}
176+
tabSelectsValue={false}
177+
onChange={(value) => {
178+
if (value) {
179+
setInstallFeedback(null);
180+
setSelectedFirmware(value);
181+
}
182+
}}
183+
/>
184+
<button
185+
type="button"
186+
className="theia-button primary"
187+
disabled={
188+
selectedFirmware === null || firmwaresFetching || installingFw
189+
}
190+
onClick={installFirmware}
191+
>
192+
Install
193+
</button>
194+
</div>
195+
<div className="row warn">
196+
<svg
197+
className="status-icon"
198+
width="18"
199+
height="18"
200+
viewBox="0 0 18 18"
201+
fill="none"
202+
xmlns="http://www.w3.org/2000/svg"
203+
>
204+
<path
205+
d="M16.9373 17.125H1.06225C0.955444 17.1246 0.850534 17.0968 0.757532 17.0442C0.66453 16.9917 0.586538 16.9162 0.531 16.825C0.476145 16.73 0.447266 16.6222 0.447266 16.5125C0.447266 16.4028 0.476145 16.295 0.531 16.2L8.4685 0.57501C8.52095 0.472634 8.60063 0.386718 8.69878 0.326724C8.79692 0.26673 8.90972 0.234985 9.02475 0.234985C9.13978 0.234985 9.25258 0.26673 9.35072 0.326724C9.44887 0.386718 9.52855 0.472634 9.581 0.57501L17.5185 16.2C17.5734 16.295 17.6022 16.4028 17.6022 16.5125C17.6022 16.6222 17.5734 16.73 17.5185 16.825C17.4588 16.9238 17.3729 17.0042 17.2703 17.0571C17.1677 17.1101 17.0524 17.1336 16.9373 17.125ZM2.081 15.875H15.9185L8.99975 2.25626L2.081 15.875Z"
206+
fill="#C11F09"
207+
/>
208+
<path
209+
d="M9 14.625C9.51777 14.625 9.9375 14.2053 9.9375 13.6875C9.9375 13.1697 9.51777 12.75 9 12.75C8.48223 12.75 8.0625 13.1697 8.0625 13.6875C8.0625 14.2053 8.48223 14.625 9 14.625Z"
210+
fill="#C11F09"
211+
/>
212+
<path
213+
d="M9 11.5C8.83424 11.5 8.67527 11.4342 8.55806 11.3169C8.44085 11.1997 8.375 11.0408 8.375 10.875V6.5C8.375 6.33424 8.44085 6.17527 8.55806 6.05806C8.67527 5.94085 8.83424 5.875 9 5.875C9.16576 5.875 9.32473 5.94085 9.44194 6.05806C9.55915 6.17527 9.625 6.33424 9.625 6.5V10.875C9.625 11.0408 9.55915 11.1997 9.44194 11.3169C9.32473 11.4342 9.16576 11.5 9 11.5Z"
214+
fill="#C11F09"
215+
/>
216+
</svg>
217+
Installation will overwrite the Sketch on the board.
218+
</div>
219+
{installFeedback === 'ok' && (
220+
<div className="row success">
221+
<svg
222+
className="status-icon"
223+
width="18"
224+
height="18"
225+
viewBox="0 0 18 18"
226+
fill="none"
227+
xmlns="http://www.w3.org/2000/svg"
228+
>
229+
<path
230+
d="M9 17.75C7.26942 17.75 5.57769 17.2368 4.13876 16.2754C2.69983 15.3139 1.57832 13.9473 0.916058 12.3485C0.253791 10.7496 0.080512 8.9903 0.418133 7.29296C0.755753 5.59563 1.58911 4.03653 2.81282 2.81282C4.03653 1.58911 5.59563 0.755753 7.29296 0.418133C8.9903 0.080512 10.7496 0.253791 12.3485 0.916058C13.9473 1.57832 15.3139 2.69983 16.2754 4.13876C17.2368 5.57769 17.75 7.26942 17.75 9C17.75 11.3206 16.8281 13.5462 15.1872 15.1872C13.5462 16.8281 11.3206 17.75 9 17.75ZM9 1.5C7.51664 1.5 6.0666 1.93987 4.83323 2.76398C3.59986 3.58809 2.63856 4.75943 2.07091 6.12988C1.50325 7.50032 1.35473 9.00832 1.64411 10.4632C1.9335 11.918 2.64781 13.2544 3.6967 14.3033C4.7456 15.3522 6.08197 16.0665 7.53683 16.3559C8.99168 16.6453 10.4997 16.4968 11.8701 15.9291C13.2406 15.3614 14.4119 14.4001 15.236 13.1668C16.0601 11.9334 16.5 10.4834 16.5 9C16.5 7.01088 15.7098 5.10323 14.3033 3.6967C12.8968 2.29018 10.9891 1.5 9 1.5Z"
231+
fill="#1DA086"
232+
/>
233+
<path
234+
d="M8.6875 5.875C9.20527 5.875 9.625 5.45527 9.625 4.9375C9.625 4.41973 9.20527 4 8.6875 4C8.16973 4 7.75 4.41973 7.75 4.9375C7.75 5.45527 8.16973 5.875 8.6875 5.875Z"
235+
fill="#1DA086"
236+
/>
237+
<path
238+
d="M12.125 12.75C12.125 12.9158 12.0592 13.0747 11.9419 13.1919C11.8247 13.3092 11.6658 13.375 11.5 13.375H6.5C6.33424 13.375 6.17527 13.3092 6.05806 13.1919C5.94085 13.0747 5.875 12.9158 5.875 12.75C5.875 12.5842 5.94085 12.4253 6.05806 12.3081C6.17527 12.1908 6.33424 12.125 6.5 12.125H8.375V8.375H7.125C6.95924 8.375 6.80027 8.30915 6.68306 8.19194C6.56585 8.07473 6.5 7.91576 6.5 7.75C6.5 7.58424 6.56585 7.42527 6.68306 7.30806C6.80027 7.19085 6.95924 7.125 7.125 7.125H9C9.16576 7.125 9.32473 7.19085 9.44194 7.30806C9.55915 7.42527 9.625 7.58424 9.625 7.75V12.125H11.5C11.6658 12.125 11.8247 12.1908 11.9419 12.3081C12.0592 12.4253 12.125 12.5842 12.125 12.75Z"
239+
fill="#1DA086"
240+
/>
241+
</svg>
242+
Firmware succesfully installed.
243+
</div>
244+
)}
245+
{installFeedback === 'fail' && (
246+
<div className="row warn">
247+
<svg
248+
className="status-icon"
249+
width="18"
250+
height="18"
251+
viewBox="0 0 18 18"
252+
fill="none"
253+
xmlns="http://www.w3.org/2000/svg"
254+
>
255+
<path
256+
d="M16.9373 17.125H1.06225C0.955444 17.1246 0.850534 17.0968 0.757532 17.0442C0.66453 16.9917 0.586538 16.9162 0.531 16.825C0.476145 16.73 0.447266 16.6222 0.447266 16.5125C0.447266 16.4028 0.476145 16.295 0.531 16.2L8.4685 0.57501C8.52095 0.472634 8.60063 0.386718 8.69878 0.326724C8.79692 0.26673 8.90972 0.234985 9.02475 0.234985C9.13978 0.234985 9.25258 0.26673 9.35072 0.326724C9.44887 0.386718 9.52855 0.472634 9.581 0.57501L17.5185 16.2C17.5734 16.295 17.6022 16.4028 17.6022 16.5125C17.6022 16.6222 17.5734 16.73 17.5185 16.825C17.4588 16.9238 17.3729 17.0042 17.2703 17.0571C17.1677 17.1101 17.0524 17.1336 16.9373 17.125ZM2.081 15.875H15.9185L8.99975 2.25626L2.081 15.875Z"
257+
fill="#C11F09"
258+
/>
259+
<path
260+
d="M9 14.625C9.51777 14.625 9.9375 14.2053 9.9375 13.6875C9.9375 13.1697 9.51777 12.75 9 12.75C8.48223 12.75 8.0625 13.1697 8.0625 13.6875C8.0625 14.2053 8.48223 14.625 9 14.625Z"
261+
fill="#C11F09"
262+
/>
263+
<path
264+
d="M9 11.5C8.83424 11.5 8.67527 11.4342 8.55806 11.3169C8.44085 11.1997 8.375 11.0408 8.375 10.875V6.5C8.375 6.33424 8.44085 6.17527 8.55806 6.05806C8.67527 5.94085 8.83424 5.875 9 5.875C9.16576 5.875 9.32473 5.94085 9.44194 6.05806C9.55915 6.17527 9.625 6.33424 9.625 6.5V10.875C9.625 11.0408 9.55915 11.1997 9.44194 11.3169C9.32473 11.4342 9.16576 11.5 9 11.5Z"
265+
fill="#C11F09"
266+
/>
267+
</svg>
268+
Installation failed. Please try again.
269+
</div>
270+
)}
271+
</>
272+
)}
273+
</div>
274+
);
275+
};
9276

10277
@injectable()
11278
export class UploadFirmwareDialogWidget extends ReactWidget {
12279
@inject(BoardsServiceProvider)
13280
protected readonly boardsServiceClient: BoardsServiceProvider;
14281

282+
@inject(ArduinoFirmwareUploader)
283+
protected readonly firmware: ArduinoFirmwareUploader;
284+
15285
constructor() {
16286
super();
17287
}
18288

19289
protected render(): React.ReactNode {
20290
return (
21-
<FirmwareUploaderComponent
22-
boardsServiceClient={this.boardsServiceClient}
23-
/>
291+
<form>
292+
<FirmwareUploaderComponent
293+
boardsServiceClient={this.boardsServiceClient}
294+
fwuploader={this.firmware}
295+
/>
296+
</form>
24297
);
25298
}
26299
}

arduino-ide-extension/src/node/arduino-firmware-uploader-impl.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
4646

4747
async list(fqbn?: string): Promise<FirmwareInfo[]> {
4848
const fqbnFlag = fqbn ? ['--fqbn', fqbn] : [];
49-
return await this.runCommand([
50-
'firmware',
51-
'list',
52-
'--format',
53-
'json',
54-
...fqbnFlag,
55-
]);
49+
const firmwares: FirmwareInfo[] =
50+
(await this.runCommand([
51+
'firmware',
52+
'list',
53+
'--format',
54+
'json',
55+
...fqbnFlag,
56+
])) || [];
57+
return firmwares.reverse();
5658
}
5759

5860
async updatableBoards(): Promise<string[]> {

0 commit comments

Comments
 (0)