Skip to content

Commit 1eba5c3

Browse files
authored
Merge pull request BruceDevices#1673 from emericklaw/flasher-previous-versions
Get firmware .bins from GitHub assets and use release tags to show all versions for flashing
2 parents 594a527 + 652ac16 commit 1eba5c3

File tree

3 files changed

+186
-90
lines changed

3 files changed

+186
-90
lines changed

.github/workflows/deploy.yml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,6 @@ jobs:
3030
- name: Install dependencies
3131
run: npm install
3232

33-
- name: Download release assets for website
34-
env:
35-
GH_TOKEN: ${{ github.token }}
36-
run: |
37-
rm -rf BetaRelease LastRelease
38-
mkdir -p BetaRelease LastRelease
39-
gh release download betaRelease --dir ./BetaRelease --clobber
40-
gh release download lastRelease --dir ./LastRelease --clobber
41-
echo "Files are now available on release tag [lastRelease](https://github.com/pr3y/Bruce/releases/tag/lastRelease)" > LastRelease/README.md
42-
echo "Files are now available on release tag [betaRelease](https://github.com/pr3y/Bruce/releases/tag/betaRelease)" > BetaRelease/README.md
43-
4433
- name: Setup Pages
4534
uses: actions/configure-pages@v5
4635
with:

src/lib/data/manifests.json

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,57 @@
11
{
22
"M5Stack": [
3-
{ "id": "m5stack-cardputer", "name": "Cardputer" },
4-
{ "id": "m5stack-cardputer-adv", "name": "Cardputer ADV" },
5-
{ "id": "m5stack-core16mb", "name": "Core16mb" },
6-
{ "id": "m5stack-core2", "name": "Core2" },
7-
{ "id": "m5stack-core4mb", "name": "Core4mb" },
8-
{ "id": "m5stack-cores3", "name": "CoreS3" },
9-
{ "id": "m5stack-cplus1_1", "name": "StickCPlus 1.1" },
10-
{ "id": "m5stack-cplus2", "name": "StickCPlus2" }
3+
{ "id": "m5stack-cardputer", "name": "Cardputer", "family": "ESP32-S3" },
4+
{ "id": "m5stack-cardputer-adv", "name": "Cardputer ADV", "family": "ESP32-S3" },
5+
{ "id": "m5stack-core16mb", "name": "Core16mb", "family": "ESP32" },
6+
{ "id": "m5stack-core2", "name": "Core2", "family": "ESP32" },
7+
{ "id": "m5stack-core4mb", "name": "Core4mb", "family": "ESP32" },
8+
{ "id": "m5stack-cores3", "name": "CoreS3", "family": "ESP32-S3" },
9+
{ "id": "m5stack-cplus1_1", "name": "StickCPlus 1.1", "family": "ESP32" },
10+
{ "id": "m5stack-cplus2", "name": "StickCPlus2", "family": "ESP32" }
1111
],
1212
"Lilygo": [
13-
{ "id": "lilygo-t-deck-pro", "name": "Lilygo T-Deck-Plus" },
14-
{ "id": "lilygo-t-deck", "name": "Lilygo T-Deck" },
15-
{ "id": "lilygo-t-display-S3-pro", "name": "Lilygo T-Display-S3 PRO" },
16-
{ "id": "lilygo-t-display-s3-mmc", "name": "Lilygo T-Display-S3 (SD_MMC)" },
17-
{ "id": "lilygo-t-display-s3-touch-mmc", "name": "Lilygo T-Display-S3 Touch (SD_MMC)" },
18-
{ "id": "lilygo-t-display-s3-touch", "name": "Lilygo T-Display-S3 Touch" },
19-
{ "id": "lilygo-t-display-s3", "name": "Lilygo T-Display-S3" },
20-
{ "id": "lilygo-t-display-ttgo", "name": "Lilygo T-Display (TTGO)" },
21-
{ "id": "lilygo-t-embed-cc1101", "name": "Lilygo T-Embed CC1101" },
22-
{ "id": "lilygo-t-embed", "name": "Lilygo T-Embed" },
23-
{ "id": "lilygo-t-watch-s3", "name": "Lilygo T-Watch S3" },
24-
{ "id": "lilygo-t-hmi", "name": "Lilygo T-HMI" },
25-
{ "id": "lilygo-t-lora-pager", "name": "Lilygo T-Lora Pager" }
13+
{ "id": "lilygo-t-deck-pro", "name": "Lilygo T-Deck-Plus", "family": "ESP32-S3" },
14+
{ "id": "lilygo-t-deck", "name": "Lilygo T-Deck", "family": "ESP32-S3" },
15+
{ "id": "lilygo-t-display-S3-pro", "name": "Lilygo T-Display-S3 PRO", "family": "ESP32-S3" },
16+
{ "id": "lilygo-t-display-s3-mmc", "name": "Lilygo T-Display-S3 (SD_MMC)", "family": "ESP32-S3" },
17+
{ "id": "lilygo-t-display-s3-touch-mmc", "name": "Lilygo T-Display-S3 Touch (SD_MMC)", "family": "ESP32-S3" },
18+
{ "id": "lilygo-t-display-s3-touch", "name": "Lilygo T-Display-S3 Touch", "family": "ESP32-S3" },
19+
{ "id": "lilygo-t-display-s3", "name": "Lilygo T-Display-S3", "family": "ESP32-S3" },
20+
{ "id": "lilygo-t-display-ttgo", "name": "Lilygo T-Display (TTGO)", "family": "ESP32" },
21+
{ "id": "lilygo-t-embed-cc1101", "name": "Lilygo T-Embed CC1101", "family": "ESP32-S3" },
22+
{ "id": "lilygo-t-embed", "name": "Lilygo T-Embed", "family": "ESP32-S3" },
23+
{ "id": "lilygo-t-watch-s3", "name": "Lilygo T-Watch S3", "family": "ESP32-S3" },
24+
{ "id": "lilygo-t-hmi", "name": "Lilygo T-HMI", "family": "ESP32-S3" },
25+
{ "id": "lilygo-t-lora-pager", "name": "Lilygo T-Lora Pager", "family": "ESP32-S3" }
2626
],
2727
"CYD": [
28-
{ "id": "CYD-2432S028", "name": "CYD-2432S028" },
29-
{ "id": "CYD-2432W328C", "name": "CYD-2432W328C" },
30-
{ "id": "CYD-2432W328C_2", "name": "CYD-2432W328C (inv colors) and CYD-2432S024C" },
31-
{ "id": "CYD-2432W328R-or-S024R", "name": "CYD-2432W328R or 2432S024R" },
32-
{ "id": "CYD-2USB", "name": "CYD-2432S028 (2USB or inverted Colors)" }
28+
{ "id": "CYD-2432S028", "name": "CYD-2432S028", "family": "ESP32" },
29+
{ "id": "CYD-2432W328C", "name": "CYD-2432W328C", "family": "ESP32" },
30+
{ "id": "CYD-2432W328C_2", "name": "CYD-2432W328C (inv colors) and CYD-2432S024C", "family": "ESP32" },
31+
{ "id": "CYD-2432W328R-or-S024R", "name": "CYD-2432W328R or 2432S024R", "family": "ESP32" },
32+
{ "id": "CYD-2USB", "name": "CYD-2432S028 (2USB or inverted Colors)", "family": "ESP32" }
3333
],
34-
"ESP32": [{ "id": "esp32-s3-devkitc-1", "name": "ESP32-S3" }],
34+
"ESP32": [{ "id": "esp32-s3-devkitc-1", "name": "ESP32-S3", "family": "ESP32-S3" }],
3535
"Custom Boards": [
36-
{ "id": "Awok-Mini", "name": "AWOK dual Mini v2" },
37-
{ "id": "Awok-Touch", "name": "AWOK dual touch v2" },
38-
{ "id": "Marauder-Mini", "name": "Marauder Mini" },
39-
{ "id": "Marauder-V4-V6", "name": "Marauder V4 or V6" },
40-
{ "id": "Marauder-v61", "name": "Marauder V6.x" },
41-
{ "id": "Marauder-v7", "name": "Marauder v7" },
42-
{ "id": "Phantom_S024R", "name": "The Phantom (Rabbit Labs)" },
43-
{ "id": "smoochiee-board", "name": "Smoochiee-Board (ESP32-S3)" }
36+
{ "id": "Awok-Mini", "name": "AWOK dual Mini v2", "family": "ESP32" },
37+
{ "id": "Awok-Touch", "name": "AWOK dual touch v2", "family": "ESP32" },
38+
{ "id": "Marauder-Mini", "name": "Marauder Mini", "family": "ESP32" },
39+
{ "id": "Marauder-V4-V6", "name": "Marauder V4 or V6", "family": "ESP32" },
40+
{ "id": "Marauder-v61", "name": "Marauder V6.x", "family": "ESP32" },
41+
{ "id": "Marauder-v7", "name": "Marauder v7", "family": "ESP32" },
42+
{ "id": "Phantom_S024R", "name": "The Phantom (Rabbit Labs)", "family": "ESP32" },
43+
{ "id": "smoochiee-board", "name": "Smoochiee-Board (ESP32-S3)", "family": "ESP32-S3" }
4444
],
4545
"Launcher": [
46-
{ "id": "LAUNCHER_CYD-2432S028", "name": "CYD-2432S028" },
47-
{ "id": "LAUNCHER_CYD-2432W328C", "name": "CYD-2432W328C" },
48-
{ "id": "LAUNCHER_CYD-2432W328R-or-S024R", "name": "CYD-2432W328R or 2432S024R" },
49-
{ "id": "LAUNCHER_CYD-2USB", "name": "CYD-2432S028 (2USB or Inverted Colors)" },
50-
{ "id": "LAUNCHER_Marauder-Mini", "name": "Marauder Mini (Launcher)" },
51-
{ "id": "LAUNCHER_Marauder-V4-V6", "name": "Marauder V4 or V6 (Launcher)" },
52-
{ "id": "LAUNCHER_Marauder-v61", "name": "Marauder V6.x (Launcher)" },
53-
{ "id": "LAUNCHER_Marauder-v7", "name": "Marauder v7 (Launcher)" },
54-
{ "id": "LAUNCHER_Phantom_S024R", "name": "The Phantom (Rabbit Labs)" },
55-
{ "id": "LAUNCHER_m5stack-cplus1_1", "name": "M5StickCPlus 1.1" }
46+
{ "id": "LAUNCHER_CYD-2432S028", "name": "CYD-2432S028", "family": "ESP32" },
47+
{ "id": "LAUNCHER_CYD-2432W328C", "name": "CYD-2432W328C", "family": "ESP32" },
48+
{ "id": "LAUNCHER_CYD-2432W328R-or-S024R", "name": "CYD-2432W328R or 2432S024R", "family": "ESP32" },
49+
{ "id": "LAUNCHER_CYD-2USB", "name": "CYD-2432S028 (2USB or Inverted Colors)", "family": "ESP32" },
50+
{ "id": "LAUNCHER_Marauder-Mini", "name": "Marauder Mini (Launcher)", "family": "ESP32" },
51+
{ "id": "LAUNCHER_Marauder-V4-V6", "name": "Marauder V4 or V6 (Launcher)", "family": "ESP32" },
52+
{ "id": "LAUNCHER_Marauder-v61", "name": "Marauder V6.x (Launcher)", "family": "ESP32" },
53+
{ "id": "LAUNCHER_Marauder-v7", "name": "Marauder v7 (Launcher)", "family": "ESP32" },
54+
{ "id": "LAUNCHER_Phantom_S024R", "name": "The Phantom (Rabbit Labs)", "family": "ESP32" },
55+
{ "id": "LAUNCHER_m5stack-cplus1_1", "name": "M5StickCPlus 1.1", "family": "ESP32" }
5656
]
5757
}

src/routes/flasher/+page.svelte

Lines changed: 141 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@
1313
});
1414
1515
function downloadFile(file: string) {
16-
const betaSelected = selectedVersion === 'Beta';
17-
const baseUrl = betaSelected
18-
? 'https://github.com/pr3y/Bruce/releases/download/betaRelease/Bruce-'
19-
: 'https://github.com/pr3y/Bruce/releases/download/lastRelease/Bruce-';
16+
const releaseTag =
17+
selectedVersion === 'Beta'
18+
? 'betaRelease'
19+
: selectedVersion === 'Latest'
20+
? latestVersionTag
21+
: document.getElementById('otherReleaseDropdown').value;
22+
23+
const fileUrl = 'https://github.com/pr3y/Bruce/releases/download/' + releaseTag + '/Bruce-' + encodeURIComponent(file) + '.bin';
2024
21-
const fileUrl = baseUrl + encodeURIComponent(file) + '.bin';
2225
const link = document.createElement('a');
2326
link.href = fileUrl;
2427
link.download = file;
@@ -33,17 +36,92 @@
3336
selectedDevice = '';
3437
}
3538
39+
function toggleRelease(version: string) {
40+
selectedVersion = version;
41+
const otherReleaseContainer = document.getElementById('otherReleaseContainer');
42+
otherReleaseContainer.style.display = version === 'Other' ? 'block' : 'none';
43+
}
44+
45+
function findDeviceById(id) {
46+
for (const category of Object.values(manifests)) {
47+
const device = category.find((d) => d.id === id);
48+
if (device) return device;
49+
}
50+
return null;
51+
}
52+
3653
function updateManifest() {
3754
if (selectedVersion && selectedDevice) {
3855
const button = document.querySelector('esp-web-install-button');
3956
if (button) {
40-
button.manifest = `${selectedVersion}Release/Bruce-${selectedDevice}.json`;
57+
const releaseTag =
58+
selectedVersion === 'Beta'
59+
? 'betaRelease'
60+
: selectedVersion === 'Latest'
61+
? latestVersionTag
62+
: document.getElementById('otherReleaseDropdown').value;
63+
64+
const manifest = {
65+
name: selectedDevice,
66+
new_install_prompt_erase: true,
67+
builds: [
68+
{
69+
chipFamily: findDeviceById(selectedDevice).family,
70+
improv: false,
71+
parts: [
72+
{
73+
path:
74+
'https://proxy.corsfix.com/?https://github.com/pr3y/Bruce/releases/download/' +
75+
releaseTag +
76+
'/Bruce-' +
77+
encodeURIComponent(selectedDevice) +
78+
'.bin',
79+
offset: 0
80+
}
81+
]
82+
}
83+
]
84+
};
85+
86+
const json = JSON.stringify(manifest);
87+
const blob = new Blob([json], { type: 'application/json' });
88+
89+
button.manifest = URL.createObjectURL(blob);
90+
4191
button.style.display = 'block';
4292
}
4393
}
4494
}
4595
4696
const active_el = (first: string, cmp: string) => (first == cmp ? 'bg-[#9B51E0] text-white' : '');
97+
98+
// Get GitHub release tags
99+
let versionTags: string[] = $state([]);
100+
let latestVersionTag: string = $state('');
101+
let loading = $state(true);
102+
let error = $state('');
103+
104+
const repo = 'pr3y/Bruce';
105+
106+
$effect(() => {
107+
fetchTags();
108+
});
109+
110+
async function fetchTags() {
111+
loading = true;
112+
error = '';
113+
try {
114+
const res = await fetch(`https://api.github.com/repos/${repo}/tags`);
115+
if (!res.ok) throw new Error('Failed to fetch tags');
116+
const data = await res.json();
117+
// Only include tags matching x.x or x.x.x
118+
versionTags = data.map((tag: { name: string }) => tag.name).filter((name: string) => /^\d+\.\d+(\.\d+)?$/.test(name));
119+
latestVersionTag = versionTags.length > 0 ? versionTags[0] : '';
120+
} catch (e) {
121+
error = e.message;
122+
}
123+
loading = false;
124+
}
47125
</script>
48126

49127
<svelte:head>
@@ -85,47 +163,73 @@
85163
<div class="flex items-center justify-center gap-4">
86164
<div class="flex gap-4">
87165
<div>
88-
<input type="radio" name="version" id="latest" value="Last" bind:group={selectedVersion} class="hidden" />
89-
<label
90-
for="latest"
166+
<button
167+
id="latest"
91168
class="{active_el(
92169
selectedVersion,
93170
'Last'
94-
)} cursor-pointer rounded-lg border-2 border-purple-500 px-5 py-2.5 text-purple-500 transition-all duration-300 ease-in-out peer-checked:bg-[#9B51E0] peer-checked:text-white hover:bg-[#9B51E0] hover:text-white w-32 min-w-[10rem] min-h-[2.5rem] flex items-center justify-center"
95-
>Latest Release</label
171+
)} flex min-h-[2.5rem] w-32 min-w-[10rem] cursor-pointer items-center justify-center rounded-lg border-2 border-purple-500 px-5 py-2.5 text-purple-500 transition-all duration-300 ease-in-out peer-checked:bg-[#9B51E0] peer-checked:text-white hover:bg-[#9B51E0] hover:text-white"
172+
onclick={() => toggleRelease('Last')}>Latest {latestVersionTag}</button
96173
>
97174
</div>
98175
<div>
99-
<input type="radio" name="version" id="beta" value="Beta" bind:group={selectedVersion} class="peer hidden" />
100-
<label
101-
for="beta"
176+
<button
177+
id="beta"
102178
class="{active_el(
103179
selectedVersion,
104180
'Beta'
105-
)} cursor-pointer rounded-lg border-2 border-purple-500 px-5 py-2.5 text-purple-500 transition-all duration-300 ease-in-out peer-checked:bg-[#9B51E0] peer-checked:text-white hover:bg-[#9B51E0] hover:text-white w-32 min-w-[10rem] min-h-[2.5rem] flex items-center justify-center"
106-
>Beta Release</label
181+
)} flex min-h-[2.5rem] w-32 min-w-[10rem] cursor-pointer items-center justify-center rounded-lg border-2 border-purple-500 px-5 py-2.5 text-purple-500 transition-all duration-300 ease-in-out peer-checked:bg-[#9B51E0] peer-checked:text-white hover:bg-[#9B51E0] hover:text-white"
182+
onclick={() => toggleRelease('Beta')}>Beta</button
183+
>
184+
</div>
185+
<div>
186+
<button
187+
id="other"
188+
class="{active_el(
189+
selectedVersion,
190+
'Other'
191+
)} flex min-h-[2.5rem] w-32 min-w-[10rem] cursor-pointer items-center justify-center rounded-lg border-2 border-purple-500 px-5 py-2.5 text-purple-500 transition-all duration-300 ease-in-out peer-checked:bg-[#9B51E0] peer-checked:text-white hover:bg-[#9B51E0] hover:text-white"
192+
onclick={() => toggleRelease('Other')}>Other</button
107193
>
194+
<div id="otherReleaseContainer" style="display: none;">
195+
{#if loading}
196+
<p>Loading tags...</p>
197+
{:else if error}
198+
<p>Error: {error}</p>
199+
{:else}
200+
<select
201+
id="otherReleaseDropdown"
202+
class="mt-2 min-h-[2.5rem] w-32 min-w-[10rem] rounded-lg border-2 border-purple-500 bg-black p-2 px-5 py-2.5 text-purple-500 transition-all duration-300 ease-in-out"
203+
>
204+
{#each versionTags as versionTag, i (versionTag)}
205+
<option value={versionTag}>{versionTag}{i === 0 ? ' (Latest)' : ''}</option>
206+
{/each}
207+
</select>
208+
{/if}
209+
</div>
108210
</div>
109211
</div>
110212
</div>
111213

112214
<div class="container">
113-
<h2 class="mt-5 flex items-center justify-center p-2 text-2xl font-bold" data-i18n="select_device_manufacturer_category_title">Select Device Manufacturer/Category</h2>
114-
<div class="mb-5 flex items-center justify-center gap-4 max-sm:flex-col flex-wrap">
215+
<h2 class="mt-5 flex items-center justify-center p-2 text-2xl font-bold" data-i18n="select_device_manufacturer_category_title">
216+
Select Device Manufacturer/Category
217+
</h2>
218+
<div class="mb-5 flex flex-wrap items-center justify-center gap-4 max-sm:flex-col">
115219
{#each Object.keys(manifests) as category}
116220
<button
117221
class="{active_el(
118222
selectedCategory,
119223
category
120-
)} cursor-pointer rounded-lg border-2 border-purple-500 px-5 py-2.5 text-purple-500 transition-all duration-300 ease-in-out peer-checked:bg-[#9B51E0] peer-checked:text-white hover:bg-[#9B51E0] hover:text-white w-32 min-w-[10rem] min-h-[2.5rem] flex items-center justify-center"
224+
)} flex min-h-[2.5rem] w-32 min-w-[10rem] cursor-pointer items-center justify-center rounded-lg border-2 border-purple-500 px-5 py-2.5 text-purple-500 transition-all duration-300 ease-in-out peer-checked:bg-[#9B51E0] peer-checked:text-white hover:bg-[#9B51E0] hover:text-white"
121225
onclick={() => toggleDeviceCategory(category)}>{category}</button
122226
>
123227
{/each}
124228
</div>
125229

126230
{#if selectedCategory}
127231
<h2 class="flex items-center justify-center p-2 text-2xl font-bold" data-i18n="select_device_title">Select Device</h2>
128-
<ul class="mb-5 flex justify-center text-center max-sm:flex-col gap-4 flex-wrap items-center">
232+
<ul class="mb-5 flex flex-wrap items-center justify-center gap-4 text-center max-sm:flex-col">
129233
{#each manifests[selectedCategory] as device}
130234
<li class="flex-shrink-0">
131235
<input
@@ -145,7 +249,7 @@
145249
class="{active_el(
146250
selectedDevice,
147251
device.id
148-
)} font-inter inline-block cursor-pointer rounded-lg border-2 border-[#9B51E0] px-[15px] py-[10px] text-center text-base text-purple-500 transition-all duration-300 ease-in-out hover:bg-[#9B51E0] hover:text-white w-48 min-w-[12rem] min-h-[3rem] flex items-center justify-center"
252+
)} font-inter flex inline-block min-h-[3rem] w-48 min-w-[12rem] cursor-pointer items-center justify-center rounded-lg border-2 border-[#9B51E0] px-[15px] py-[10px] text-center text-base text-purple-500 transition-all duration-300 ease-in-out hover:bg-[#9B51E0] hover:text-white"
149253
for={device.id}>{device.name}</label
150254
>
151255
</li>
@@ -156,21 +260,23 @@
156260

157261
<div class="container">
158262
{#if selectedDevice}
159-
<h2 class="mt-5 flex items-center justify-center p-2 text-2xl font-bold" data-i18n="select_how_to_install_firmware_title">Choose How to Install Firmware</h2>
160-
<p class="mb-5 text-center">
161-
<esp-web-install-button style={selectedDevice ? 'display:block' : 'display:none'}>
162-
<button
163-
slot="activate"
164-
class="font-inter inline-block cursor-pointer rounded-lg border-2 border-purple-500 bg-purple-500 px-[15px] py-[10px] text-base font-semibold text-white transition-all duration-300 ease-in-out hover:scale-105 hover:border-purple-600 hover:bg-purple-600"
165-
>
166-
CONNECT TO DEVICE
167-
</button>
168-
</esp-web-install-button>
169-
</p>
170-
263+
<h2 class="mt-5 flex items-center justify-center p-2 text-2xl font-bold" data-i18n="select_how_to_install_firmware_title">
264+
Choose How to Install Firmware
265+
</h2>
266+
<p class="mb-5 text-center">
267+
<esp-web-install-button style={selectedDevice ? 'display:block' : 'display:none'}>
268+
<button
269+
slot="activate"
270+
class="font-inter inline-block cursor-pointer rounded-lg border-2 border-purple-500 bg-purple-500 px-[15px] py-[10px] text-base font-semibold text-white transition-all duration-300 ease-in-out hover:scale-105 hover:border-purple-600 hover:bg-purple-600"
271+
>
272+
CONNECT TO DEVICE
273+
</button>
274+
</esp-web-install-button>
275+
</p>
276+
171277
<p class="mt-3 mb-5 text-center">
172278
<button
173-
class="font-inter inline-block cursor-pointer rounded-lg border-2 border-[#9B51E0] px-[15px] py-[10px] text-center text-base text-[#9B51E0] transition-all duration-300 ease-in-out hover:scale-105 hover:bg-[#9B51E0] hover:text-white hover:font-semibold"
279+
class="font-inter inline-block cursor-pointer rounded-lg border-2 border-[#9B51E0] px-[15px] py-[10px] text-center text-base text-[#9B51E0] transition-all duration-300 ease-in-out hover:scale-105 hover:bg-[#9B51E0] hover:font-semibold hover:text-white"
174280
onclick={() => downloadFile(selectedDevice)}
175281
>
176282
DOWNLOAD FIRMWARE .BIN
@@ -180,6 +286,7 @@
180286
</div>
181287

182288
<div class="container">&nbsp;</div>
289+
183290
<style>
184291
.container {
185292
width: 90%;

0 commit comments

Comments
 (0)