mirror of
https://github.com/Laxilef/OTGateway.git
synced 2026-11-12 01:14:45 +05:00
feat: Implement W5500 SPI ethernet support (#286)
* feat: implement feature gate & w5500 SPI ethernet * feat: eth conn restart handling, wifi-ap connection type, BLE sensor gate * fix: bring eth down when disabled * fix: disable WiFi scan when ethernet is active * feat: refactor uplink types into separate classes * fix: compare enum, not string * chore: remove dead code * fix: remove out of scope persistNow(), move w5500 to networksettings, add base for future ethernet implementations * fix: keep wifi armed while reconnecting, reset IP when moving from static to DHCP, refactor wifi loop to use return * fix: clear out connectionType string for enum
This commit is contained in:
1 parent
dfbd297f90
commit
f9a814ca11
25 files changed
+1801
-450
No files matched your search
@@ -50,15 +50,19 @@
|
||||
<th scope="row" data-i18n>network.params.mac</th>
|
||||
<td><b id="network-mac"></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" data-i18n>network.params.connection</th>
|
||||
<td><b id="network-connection"></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" data-i18n>network.wifi.connected</th>
|
||||
<td><i id="network-connected"></i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr id="network-ssid-row">
|
||||
<th scope="row" data-i18n>network.wifi.ssid</th>
|
||||
<td><b id="network-ssid"></b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr id="network-signal-row">
|
||||
<th scope="row" data-i18n>network.wifi.signal</th>
|
||||
<td><b id="network-signal"></b> %</td>
|
||||
</tr>
|
||||
@@ -192,6 +196,7 @@
|
||||
|
||||
setValue('#network-hostname', result.network.hostname);
|
||||
setValue('#network-mac', result.network.mac);
|
||||
setValue('#network-connection', i18n(`network.params.connectionType.${result.network.connectionType}`));
|
||||
setStatus(
|
||||
'#network-connected',
|
||||
result.network.connected ? "success" : "error",
|
||||
@@ -199,6 +204,14 @@
|
||||
);
|
||||
setValue('#network-ssid', result.network.ssid);
|
||||
setValue('#network-signal', result.network.signalQuality);
|
||||
|
||||
// SSID and signal strength only describe the uplink when WiFi carries it; with
|
||||
// ethernet up the radio is parked and both values are meaningless
|
||||
const wifiUplink = result.network.connectionType === "wifi";
|
||||
|
||||
document.getElementById("network-ssid-row")?.classList.toggle("hidden", !wifiUplink);
|
||||
document.getElementById("network-signal-row")?.classList.toggle("hidden", !wifiUplink);
|
||||
|
||||
setValue('#network-ip', result.network.ip);
|
||||
setValue('#network-subnet', result.network.subnet);
|
||||
setValue('#network-gateway', result.network.gateway);
|
||||
|
||||
+115
-6
@@ -79,6 +79,7 @@
|
||||
</div>
|
||||
</article>
|
||||
|
||||
|
||||
<article>
|
||||
<div>
|
||||
<hgroup>
|
||||
@@ -100,6 +101,7 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p id="scan-unavailable" class="hidden" data-i18n>network.scan.unavailable</p>
|
||||
<button type="submit" data-i18n>button.refresh</button>
|
||||
</form>
|
||||
|
||||
@@ -162,6 +164,62 @@
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="ethernet-section" class="hidden">
|
||||
<div>
|
||||
<hgroup>
|
||||
<h3 data-i18n>network.section.ethernet</h3>
|
||||
<p></p>
|
||||
</hgroup>
|
||||
|
||||
<div id="ethernet-settings-busy" aria-busy="true"></div>
|
||||
<form action="/api/network/settings" id="ethernet-settings" class="hidden">
|
||||
<label>
|
||||
<input type="checkbox" name="ethernet[enabled]" value="true">
|
||||
<span data-i18n>network.ethernet.enable</span>
|
||||
</label>
|
||||
<small id="ethernet-not-configured" class="hidden" data-i18n>network.ethernet.notConfigured</small>
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>network.ethernet.clk</span>
|
||||
<input type="number" inputmode="numeric" name="ethernet[pins][clk]" min="0" max="254" step="1">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>network.ethernet.mosi</span>
|
||||
<input type="number" inputmode="numeric" name="ethernet[pins][mosi]" min="0" max="254" step="1">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>network.ethernet.miso</span>
|
||||
<input type="number" inputmode="numeric" name="ethernet[pins][miso]" min="0" max="254" step="1">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<label>
|
||||
<span data-i18n>network.ethernet.cs</span>
|
||||
<input type="number" inputmode="numeric" name="ethernet[pins][cs]" min="0" max="254" step="1">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>network.ethernet.irq</span>
|
||||
<input type="number" inputmode="numeric" name="ethernet[pins][irq]" min="0" max="254" step="1">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span data-i18n>network.ethernet.rst</span>
|
||||
<input type="number" inputmode="numeric" name="ethernet[pins][rst]" min="0" max="254" step="1">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<small data-i18n>network.ethernet.note</small>
|
||||
|
||||
<button type="submit" data-i18n>button.save</button>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<footer class="container">
|
||||
@@ -190,6 +248,29 @@
|
||||
setInputValue("[name='staticConfig[dns]']", data.staticConfig.dns);
|
||||
setBusy('#network-settings-busy', '#network-settings', false);
|
||||
|
||||
const ethernet = data.ethernet || {};
|
||||
const ethernetConfigured = !!ethernet.configured;
|
||||
const ethernetPins = ethernet.pins || {};
|
||||
const pinValue = (value) => (value ?? 255) < 255 ? value : '';
|
||||
|
||||
setInputValue("[name='ethernet[pins][clk]']", pinValue(ethernetPins.clk));
|
||||
setInputValue("[name='ethernet[pins][mosi]']", pinValue(ethernetPins.mosi));
|
||||
setInputValue("[name='ethernet[pins][miso]']", pinValue(ethernetPins.miso));
|
||||
setInputValue("[name='ethernet[pins][cs]']", pinValue(ethernetPins.cs));
|
||||
setInputValue("[name='ethernet[pins][irq]']", pinValue(ethernetPins.irq));
|
||||
setInputValue("[name='ethernet[pins][rst]']", pinValue(ethernetPins.rst));
|
||||
|
||||
setCheckboxValue("[name='ethernet[enabled]']", ethernetConfigured ? !!ethernet.enabled : false);
|
||||
|
||||
const ethernetCheckbox = document.querySelector("[name='ethernet[enabled]']");
|
||||
if (ethernetCheckbox) {
|
||||
ethernetCheckbox.disabled = !ethernetConfigured;
|
||||
}
|
||||
|
||||
document.getElementById('ethernet-not-configured')?.classList.toggle('hidden', ethernetConfigured);
|
||||
|
||||
setBusy('#ethernet-settings-busy', '#ethernet-settings', false);
|
||||
|
||||
setInputValue("[name='sta[ssid]']", data.sta.ssid);
|
||||
setInputValue("[name='sta[password]']", data.sta.password);
|
||||
setInputValue("[name='sta[channel]']", data.sta.channel);
|
||||
@@ -201,21 +282,49 @@
|
||||
setBusy('#ap-settings-busy', '#ap-settings', false);
|
||||
};
|
||||
|
||||
const applyCapabilities = (info) => {
|
||||
// pages and firmware ship together: the section stays hidden unless the build says it can do ethernet
|
||||
const supported = info?.features?.ethernet === true;
|
||||
|
||||
document.getElementById('ethernet-section')?.classList.toggle('hidden', !supported);
|
||||
|
||||
return supported;
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/network/settings", {
|
||||
cache: "no-cache",
|
||||
credentials: "include"
|
||||
});
|
||||
|
||||
const [response, infoResponse] = await Promise.all([
|
||||
fetch("/api/network/settings", {
|
||||
cache: "no-cache",
|
||||
credentials: "include"
|
||||
}),
|
||||
fetch("/api/info", {
|
||||
cache: "no-cache",
|
||||
credentials: "include"
|
||||
})
|
||||
]);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Response not valid');
|
||||
}
|
||||
|
||||
const info = infoResponse.ok ? await infoResponse.json() : null;
|
||||
const ethernetSupported = applyCapabilities(info);
|
||||
|
||||
const result = await response.json();
|
||||
fillData(result);
|
||||
|
||||
setupForm('#network-settings', fillData, ['hostname']);
|
||||
setupNetworkScanForm('#network-scan', '#networks');
|
||||
|
||||
// scanning needs the radio, which stays parked while ethernet carries the uplink
|
||||
const radioParked = info?.network?.connectionType === 'ethernet';
|
||||
setupNetworkScanForm('#network-scan', '#networks', !radioParked);
|
||||
document.querySelector('#network-scan button[type="submit"]').disabled = radioParked;
|
||||
document.querySelector('#scan-unavailable').classList.toggle('hidden', !radioParked);
|
||||
|
||||
if (ethernetSupported) {
|
||||
setupForm('#ethernet-settings', fillData);
|
||||
}
|
||||
|
||||
setupForm('#sta-settings', fillData, ['sta.ssid', 'sta.password']);
|
||||
setupForm('#ap-settings', fillData, ['ap.ssid', 'ap.password']);
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
<p></p>
|
||||
</hgroup>
|
||||
|
||||
<small id="ble-not-supported" class="hidden" data-i18n>settings.note.bleDevice</small>
|
||||
|
||||
<details id="template" class="sensor hidden" data-id="" data-preloaded="0">
|
||||
<summary><b>#<span class="pos"></span>: <span class="name"></span></b></summary>
|
||||
|
||||
@@ -117,7 +119,7 @@
|
||||
|
||||
<option value="50" data-i18n>sensors.types.ntcTemp</option>
|
||||
<option value="51" data-i18n>sensors.types.dallasTemp</option>
|
||||
<option value="52" data-i18n>sensors.types.bluetooth</option>
|
||||
<option value="52" data-requires="ble" disabled data-i18n>sensors.types.bluetooth</option>
|
||||
<option value="253" data-i18n>sensors.types.heatSetpointTemp</option>
|
||||
<option value="254" data-i18n>sensors.types.manual</option>
|
||||
<option value="255" data-i18n>sensors.types.notConfigured</option>
|
||||
@@ -206,16 +208,38 @@
|
||||
const container = document.querySelector("article");
|
||||
const templateNode = container.querySelector("#template");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/sensors", {
|
||||
cache: "no-cache",
|
||||
credentials: "include"
|
||||
let bleSupported = false;
|
||||
|
||||
const applyCapabilities = (info, root) => {
|
||||
// pages and firmware ship together: BLE stays disabled unless the build says it has the stack
|
||||
bleSupported = info?.features?.ble === true;
|
||||
|
||||
root.querySelectorAll('[data-requires="ble"]').forEach((option) => {
|
||||
option.disabled = !bleSupported;
|
||||
});
|
||||
|
||||
document.getElementById("ble-not-supported")?.classList.toggle("hidden", bleSupported);
|
||||
};
|
||||
|
||||
try {
|
||||
const [response, infoResponse] = await Promise.all([
|
||||
fetch("/api/sensors", {
|
||||
cache: "no-cache",
|
||||
credentials: "include"
|
||||
}),
|
||||
fetch("/api/info", {
|
||||
cache: "no-cache",
|
||||
credentials: "include"
|
||||
})
|
||||
]);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Response not valid");
|
||||
}
|
||||
|
||||
const info = infoResponse.ok ? await infoResponse.json() : null;
|
||||
applyCapabilities(info, templateNode);
|
||||
|
||||
const result = await response.json();
|
||||
for (const sensorId in result) {
|
||||
const sensorNode = templateNode.cloneNode(true);
|
||||
@@ -287,6 +311,12 @@
|
||||
|
||||
// ble
|
||||
case 52:
|
||||
if (!bleSupported) {
|
||||
parentGpio.classList.add("hidden");
|
||||
parentAddress.classList.add("hidden");
|
||||
break;
|
||||
}
|
||||
|
||||
parentGpio.classList.add("hidden");
|
||||
parentAddress.classList.remove("hidden");
|
||||
address.setAttribute("pattern", "([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}");
|
||||
|
||||
Reference in new issue
Block a user