mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-27 00:39:56 +00:00
fix(picker): use release JSON for OTA and logging
This commit is contained in:
@@ -576,6 +576,39 @@
|
||||
});
|
||||
}
|
||||
|
||||
function applyReleaseRuntimeCapabilities(profile, controls) {
|
||||
if (["none", "lora-receiver", "lora-source"].includes(controls.otaRole)) {
|
||||
profile.ota = controls.otaRole;
|
||||
}
|
||||
|
||||
const modes = controls.loggingModes;
|
||||
const source = controls.loggingSource;
|
||||
const sourceMatches = !source || (typeof source === "string" &&
|
||||
/^[0-9a-f]{40}$/i.test(source) && profile.files.length > 0 &&
|
||||
profile.files.every(function (file) {
|
||||
return file.name.toLowerCase().replace(/\.(bin|hex|uf2|zip)$/i, "")
|
||||
.replace(/-merged$/, "")
|
||||
.endsWith("-" + source.slice(0, 8).toLowerCase());
|
||||
}));
|
||||
if (sourceMatches && Array.isArray(modes)) {
|
||||
const key = modes.join(",");
|
||||
if (key === "none") {
|
||||
profile.logging = "none";
|
||||
profile.loggingModes = ["none"];
|
||||
} else if (key === "none,usb" && controls.loggingControl === "usb.logging") {
|
||||
profile.logging = "usb-runtime";
|
||||
profile.loggingModes = modes.slice();
|
||||
} else if (key === "none,usb,wifi,both" &&
|
||||
controls.loggingControl === "logging.output") {
|
||||
profile.logging = "runtime";
|
||||
profile.loggingModes = modes.slice();
|
||||
}
|
||||
}
|
||||
if (typeof controls.dedicatedUsbLogging === "boolean") {
|
||||
profile.dedicatedUsbLogging = controls.dedicatedUsbLogging;
|
||||
}
|
||||
}
|
||||
|
||||
function hardwareFamilyFor(hardware, hardwareNames) {
|
||||
const value = String(hardware || "");
|
||||
const lowerValue = value.toLowerCase();
|
||||
@@ -723,7 +756,9 @@
|
||||
if (controlData && controlData.familyTag === releaseSet.familyTag &&
|
||||
controlData.profiles && controlData.profiles[profile.target]) {
|
||||
profile.controls = controlData.profiles[profile.target];
|
||||
if (isFullCompanion(profile) && profile.controls.mqtt) {
|
||||
applyReleaseRuntimeCapabilities(profile, profile.controls);
|
||||
if (isFullCompanion(profile) && profile.controls.mqtt &&
|
||||
!Array.isArray(profile.controls.loggingModes)) {
|
||||
profile.loggingModes = ["none", "usb", "wifi", "both"];
|
||||
}
|
||||
if (profile.controls.rs232 && profile.role === "repeater" && profile.mode === "standard") {
|
||||
|
||||
@@ -17,7 +17,7 @@ The picker reads public release metadata from GitHub. It does not upload device
|
||||
information. Hardware names, target names, and download links come directly
|
||||
from the published firmware assets.
|
||||
|
||||
<div class="firmware-picker" data-firmware-picker data-release-repo="mikecarper/MeshCore" data-controls-url="../_data/firmware_controls.json?v=1.17.1.6" data-share-url="https://mikecarper.github.io/MeshCore/firmware_picker/">
|
||||
<div class="firmware-picker" data-firmware-picker data-release-repo="mikecarper/MeshCore" data-controls-url="../_data/firmware_controls.json?v=1.17.1.6-runtime-2" data-share-url="https://mikecarper.github.io/MeshCore/firmware_picker/">
|
||||
<div class="firmware-picker-intro" role="note">
|
||||
<strong>Current release set</strong>
|
||||
<p data-role="release-set">Loading release information...</p>
|
||||
@@ -438,6 +438,11 @@ Hardware-specific controls are enabled only when `_data/firmware_controls.json`
|
||||
matches the selected release family and exact target. If that metadata is
|
||||
missing or belongs to another release, the picker retains basic role/logging
|
||||
directions and links the complete guide without inventing hardware support.
|
||||
For 1.17.1.6, that JSON also records the verified LoRa OTA role of each image
|
||||
and USB logging modes where the packaged application was checked. Logging
|
||||
metadata is tied to the source hash of its firmware file. The normal iKoka
|
||||
30 dBm repeater has USB logging and LoRa OTA; its compact no-external-sensors
|
||||
image has LoRa OTA without logging.
|
||||
Capacity directions come from each qualified profile's recorded reductions and
|
||||
are tied to the source hash of the selected download. The older 1.17.1.5 USB
|
||||
sleep workaround stays limited to that release.
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ extra_css:
|
||||
|
||||
extra_javascript:
|
||||
- https://unpkg.com/leaflet@1.9.4/dist/leaflet.js
|
||||
- _javascript/firmware_picker.js
|
||||
- _javascript/firmware_picker.js?v=20260921-1
|
||||
- _javascript/telemetry_decoder.js
|
||||
- _javascript/management_decoder.js
|
||||
- _javascript/filter_tool.js
|
||||
|
||||
@@ -1012,7 +1012,7 @@ console.log('role-specific runtime directions tests passed');
|
||||
// above keep the older release's USB and capacity workarounds intact.
|
||||
const currentControls = require('../docs/_data/firmware_controls.json');
|
||||
const currentAssets = Object.entries(currentControls.profiles).map(([target, info]) => {
|
||||
const source = info.memorySource || currentControls.source;
|
||||
const source = info.loggingSource || info.memorySource || currentControls.source;
|
||||
const tag = currentControls.familyTag.replace(/-[0-9a-f]{8}$/, '-' + source.slice(0, 8));
|
||||
return asset(target + '-' + tag + (info.platform === 'NRF52_PLATFORM' ? '.uf2' : '.bin'));
|
||||
});
|
||||
@@ -1021,6 +1021,37 @@ const currentCatalog = picker.buildCatalog([
|
||||
], currentControls);
|
||||
assert.strictEqual(currentCatalog.rows.length, currentAssets.length);
|
||||
assert(currentCatalog.profiles.every(profile => profile.controls && profile.chipFamily !== 'unknown'));
|
||||
const ikokaNormal = currentCatalog.profiles.find(profile =>
|
||||
profile.target === 'ikoka_stick_nrf_30dbm_repeater');
|
||||
const ikokaLean = currentCatalog.profiles.find(profile =>
|
||||
profile.target === 'ikoka_stick_nrf_30dbm_repeater_lora_ota_no_external_sensors');
|
||||
assert.strictEqual(ikokaNormal.ota, 'lora-receiver');
|
||||
assert.strictEqual(ikokaNormal.logging, 'usb-runtime');
|
||||
assert.deepStrictEqual(ikokaNormal.loggingModes, ['none', 'usb']);
|
||||
assert.deepStrictEqual(picker.runtimeDirections(ikokaNormal, {logging: 'usb'})[0].actions[0].commands,
|
||||
['set usb.logging on']);
|
||||
assert.strictEqual(ikokaLean.ota, 'lora-receiver');
|
||||
assert.strictEqual(ikokaLean.logging, 'none');
|
||||
assert.deepStrictEqual(ikokaLean.loggingModes, ['none']);
|
||||
const ikokaUrl = 'https://example.com/firmware_picker/?chipFamily=nrf52&hardwareFamily=ikoka_stick_nrf_30dbm&hardware=ikoka_stick_nrf_30dbm&role=repeater&variant=default&install=zip&chipAuto=1';
|
||||
const ikokaRelease = release(currentControls.familyTag, '2026-09-13T00:00:00Z',
|
||||
[ikokaNormal, ikokaLean].map(profile => {
|
||||
const source = currentControls.profiles[profile.target].loggingSource;
|
||||
const tag = currentControls.familyTag.replace(/-[0-9a-f]{8}$/, '-' + source.slice(0, 8));
|
||||
return asset(profile.target + '-ota-' + tag + '.zip');
|
||||
}));
|
||||
const ikokaCatalog = picker.buildCatalog([ikokaRelease], currentControls);
|
||||
const ikokaSelection = picker.selectionFromUrl(ikokaUrl, ikokaCatalog.profiles);
|
||||
assert.deepStrictEqual(ikokaSelection.unavailable, []);
|
||||
assert.deepStrictEqual(ikokaCatalog.profiles.filter(profile =>
|
||||
picker.profileMatchesFacets(profile, ikokaSelection.filters)).map(profile => profile.target),
|
||||
['ikoka_stick_nrf_30dbm_repeater']);
|
||||
const mismatchedIkoka = picker.buildCatalog([
|
||||
release(currentControls.familyTag, '2026-09-13T00:00:00Z', [
|
||||
asset('ikoka_stick_nrf_30dbm_repeater-ota-' + currentControls.familyTag + '.zip'),
|
||||
]),
|
||||
], currentControls).profiles[0];
|
||||
assert.strictEqual(mismatchedIkoka.logging, 'none', 'Logging metadata must match the published build source');
|
||||
const capacityProfiles = currentCatalog.profiles.filter(profile => profile.controls.memoryNote);
|
||||
assert(capacityProfiles.length > 0, 'Regeneration must preserve capacity directions');
|
||||
for (const profile of capacityProfiles) {
|
||||
|
||||
Reference in New Issue
Block a user