Region setting (WiFi country, locale, keyboard) on the Super+W screen and Web UI

kiosk-wifi-helper gains region-status/region-set. The country is persisted as
a cfg80211 regdom module option and in the WiFi netplan file; locale and
keyboard go through localectl, validated against what's installed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A97KUggSE3rBL8veDM5Mr4
This commit is contained in:
Claude
2026-10-16 17:48:41 +00:00
parent 9221fc85aa
commit d0d39e48bb
9 changed files with 331 additions and 2 deletions
+58
View File
@@ -643,6 +643,63 @@ document.getElementById('run-upgrade').addEventListener('click', async (e) => {
}
});
/* ---------------------------------------------------------------------- */
/* Region */
/* ---------------------------------------------------------------------- */
function fillSelect(sel, items, current) {
sel.textContent = '';
const list = items.slice();
if (current && !list.includes(current)) list.unshift(current);
for (const v of list) {
const o = document.createElement('option');
o.value = v;
o.textContent = v;
o.selected = v === current;
sel.appendChild(o);
}
}
async function loadRegion() {
try {
const res = await fetch('/api/region');
const r = await res.json();
if (!res.ok) throw new Error(r.error || 'Could not read the region');
document.getElementById('region-country').value = r.country || '';
fillSelect(document.getElementById('region-locale'), r.locales || [], r.locale);
fillSelect(document.getElementById('region-keyboard'), r.keyboards || [], r.keyboard);
} catch (e) {
showMessage(e.message, true);
}
}
document.getElementById('region-form').addEventListener('submit', async (ev) => {
ev.preventDefault();
try {
const res = await fetch('/api/region', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
country: document.getElementById('region-country').value.trim(),
locale: document.getElementById('region-locale').value,
keyboard: document.getElementById('region-keyboard').value,
}),
});
const r = await res.json();
if (!res.ok) throw new Error(r.error || 'Could not save');
showMessage(r.restart ? 'Region saved - restart for the locale and keyboard to apply' : 'Region saved');
loadRegion();
} catch (e) {
showMessage(e.message, true);
}
});
document.getElementById('region-from-locale').addEventListener('click', () => {
const m = /_([A-Z]{2})/.exec(document.getElementById('region-locale').value);
if (m) document.getElementById('region-country').value = m[1];
else showMessage("That locale doesn't name a country", true);
});
/* ---------------------------------------------------------------------- */
/* Init */
/* ---------------------------------------------------------------------- */
@@ -657,6 +714,7 @@ async function init() {
}
await loadAddonStatus();
renderAddons();
loadRegion();
}
init();
+33
View File
@@ -36,6 +36,10 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2 3 7l9 5 9-5-9-5Z"/><path d="m3 12 9 5 9-5M3 17l9 5 9-5"/></svg>
Addons
</button>
<button class="nav-item" data-page="region">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>
Region
</button>
<button class="nav-item" data-page="update">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-2.6-6.4M21 4v5h-5"/></svg>
Update
@@ -192,6 +196,35 @@
<div id="addons-list"></div>
</section>
<!-- Region -->
<section class="page" id="page-region">
<div class="page-header">
<h1>Region</h1>
<p>WiFi country, system language and keyboard layout. The WiFi country decides which channels the adapter may join - without it many 5 GHz networks show up in scans but won't connect.</p>
</div>
<div class="card">
<form id="region-form">
<label>
WiFi country (2 letters, e.g. US)
<input type="text" id="region-country" maxlength="2" autocomplete="off" spellcheck="false" style="text-transform: uppercase">
</label>
<label>
Language / locale
<select id="region-locale"></select>
</label>
<label>
Keyboard layout
<select id="region-keyboard"></select>
</label>
<div class="button-row">
<button type="submit">Save region</button>
<button type="button" id="region-from-locale" class="secondary">Country from locale</button>
</div>
<p class="hint">Locale and keyboard apply after a restart; the WiFi country applies immediately.</p>
</form>
</div>
</section>
<!-- Update -->
<section class="page" id="page-update">
<div class="page-header">