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:
@@ -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();
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user