Distinguish T-LoRa picker profiles and check all board choices

This commit is contained in:
mikecarper committed 2026-10-24 15:31:14 -07:00
1 parent e0028d9630
commit 3a2d3ecf7a
3 files changed
+107 -1

No files matched your search

+19
View File
@@ -1097,6 +1097,23 @@
profile.loggingModes = modes.slice();
}
}
// T-LoRa has two distinct Full images because its UART bridge and MQTT
// client cannot fit together. USB/off logging must not collapse them into
// one firmware-profile button. Qualify the split against the exact files.
if (source && sourceMatches && source === profile.controlsSource &&
controls.platform === "ESP32_PLATFORM" && controls.espnowBridge === true) {
const loggingKey = Array.isArray(modes) ? modes.join(",") : "";
if (/^LilyGo_TLora_V2_1_1_6_repeater-full-(?:logging|usb-wifi)$/i.test(profile.target) &&
controls.loggingControl === "usb.logging" && loggingKey === "none,usb" && controls.mqtt === false &&
controls.rs232Merged === true && controls.rs232Source === source &&
releaseBoundControl(profile, "rs232")) {
profile.variant = "rs232-bridge";
} else if (/^LilyGo_TLora_V2_1_1_6_repeater_observer_mqtt_?-full-usb-wifi$/i.test(profile.target) &&
controls.loggingControl === "logging.output" && loggingKey === "none,usb,wifi,both" &&
controls.mqtt === true && controls.rs232 === false) {
profile.variant = "mqtt-observer";
}
}
if (typeof controls.dedicatedUsbLogging === "boolean") {
profile.dedicatedUsbLogging = controls.dedicatedUsbLogging;
}
@@ -1794,6 +1811,8 @@
function humanizeVariant(value, hardware) {
if (!value || value === "default") return "Default";
if (value === "rs232-bridge") return "RS-232 bridge";
if (value === "mqtt-observer") return "Wi-Fi MQTT observer";
const tokenLabels = {
ps: "Power save",
rx: "RX",
+1 -1
View File
@@ -135,7 +135,7 @@ extra_javascript:
- _javascript/hosted_doc_links.js?v=20261007-1
- _javascript/notification_builder.js?v=20261006-channel9
- https://unpkg.com/leaflet@1.9.4/dist/leaflet.js
- _javascript/firmware_picker.js?v=20261024-embedded-rak-profiles
- _javascript/firmware_picker.js?v=20261024-board-profile-audit
- _javascript/telemetry_decoder.js?v=20261022-noise-history
- _javascript/management_decoder.js
- _javascript/filter_tool.js?v=20261002-hashbytes-2plus
+87
View File
@@ -2006,6 +2006,93 @@ assert(!tloraObserverDirections.some(section => /^RS232 bridge/.test(section.tit
assert(!commands(tloraObserverDirections).some(command => command.startsWith('set rs232.')));
console.log('T-LoRa RAM exception preserves separate UART and MQTT Full picker choices');
const tlora118Targets = ['LilyGo_TLora_V2_1_1_6_repeater-full-logging', tloraObserverTarget];
const tlora118Profiles = tlora118Targets.map(target => publishedCatalog.profiles.find(profile => profile.target === target));
assert(tlora118Profiles.every(Boolean), 'The current release must exercise both qualified T-LoRa images');
assert.deepStrictEqual(tlora118Profiles.map(profile => profile.variant), ['rs232-bridge', 'mqtt-observer']);
assert(picker.firmwareProfileLabel(tlora118Profiles[0]).includes('RS-232 bridge'));
assert(picker.firmwareProfileLabel(tlora118Profiles[1]).includes('Wi-Fi MQTT observer'));
for (const logging of ['none', 'usb']) {
for (const mode of ['standard', 'espnow']) {
const filters = {hardware: 'LilyGo_TLora_V2_1_1_6', role: 'repeater', logging, mode};
const choices = picker.firmwareProfileChoices(publishedCatalog.profiles, filters);
assert.strictEqual(choices.length, 2, 'Shared T-LoRa logging/transport choices must still select two real images');
assert.strictEqual(new Set(choices.map(choice => choice.value)).size, 2);
assert.strictEqual(new Set(choices.map(choice => choice.label)).size, 2);
for (const profile of tlora118Profiles) {
assert(picker.profileMatchesFacets(profile, filters));
const selected = {...filters, ota: profile.ota, feature: profile.feature, variant: profile.variant, install: 'bin'};
const url = picker.shortSelectionUrl('https://mikecarper.github.io/MeshCore/firmware_picker/', selected, true);
const restored = picker.selectionFromUrl(url, publishedCatalog.profiles);
assert.deepStrictEqual(restored.unavailable, []);
assert.deepStrictEqual(publishedCatalog.profiles.filter(item =>
picker.profileMatchesFacets(item, restored.filters)).map(item => item.target), [profile.target],
'The new T-LoRa profile URL must identify exactly the chosen application');
}
}
}
for (const logging of ['wifi', 'both']) {
assert.deepStrictEqual(tlora118Profiles.filter(profile =>
picker.profileMatchesFacets(profile, {logging})).map(profile => profile.target), [tlora118Targets[1]],
'Only the observer image supports direct Wi-Fi MQTT output');
}
assert.deepStrictEqual(tlora118Profiles.filter(profile =>
picker.profileMatchesFacets(profile, {mode: 'rs232'})).map(profile => profile.target), [tlora118Targets[0]],
'Only the normal T-LoRa image supports the UART bridge');
for (const profile of tlora118Profiles) {
const original = publishedAssets.find(file => file.name.startsWith(profile.target + '-'));
assert(original && profile.files.some(file => file.name === original.name && file.url === original.browser_download_url));
assert(publishedCatalog.rows.some(file => file.name === original.name && file.url === original.browser_download_url),
'T-LoRa profile labels must preserve exact published filenames and URLs');
}
for (const target of tlora118Targets) {
const normal = target === tlora118Targets[0];
const faults = ['missing_logging_source', 'stale_logging_source', 'missing_logging_modes',
'wrong_logging_control', 'missing_controls_source', 'wrong_hardware'];
if (normal) faults.push('missing_uart_source', 'stale_uart_source', 'unmerged_uart');
for (const fault of faults) {
const info = JSON.parse(JSON.stringify(publishedControls.profiles[target]));
let changedTarget = target;
let source = publishedControls.source;
if (fault === 'missing_logging_source') delete info.loggingSource;
if (fault === 'stale_logging_source') info.loggingSource = 'b'.repeat(40);
if (fault === 'missing_logging_modes') delete info.loggingModes;
if (fault === 'wrong_logging_control') info.loggingControl = normal ? 'logging.output' : 'usb.logging';
if (fault === 'missing_controls_source') source = undefined;
if (fault === 'wrong_hardware') changedTarget = target.replace('V2_1_1_6', 'V2_0');
if (fault === 'missing_uart_source') delete info.rs232Source;
if (fault === 'stale_uart_source') info.rs232Source = 'b'.repeat(40);
if (fault === 'unmerged_uart') info.rs232Merged = false;
const partial = picker.buildCatalog([
release(publishedControls.familyTag, '2026-10-24T00:00:00Z',
[asset(changedTarget + '-' + publishedControls.familyTag + '.bin')]),
], {...publishedControls, source, profiles: {[changedTarget]: info}});
assert.strictEqual(partial.profiles.length, 1);
assert(!['rs232-bridge', 'mqtt-observer'].includes(partial.profiles[0].variant),
'T-LoRa transport variants require exact source-qualified capability proof: ' + target + ': ' + fault);
}
}
// Distinct images can share a profile tuple only when another exposed selector
// actually separates them, as with the Tracker V2's different connection modes.
function profilesOverlap(left, right, field) {
const values = field === 'install' ? left.installKinds : picker.profileFieldValues(left, field);
const other = field === 'install' ? right.installKinds : picker.profileFieldValues(right, field);
return values.some(value => other.includes(value));
}
for (let leftIndex = 0; leftIndex < publishedCatalog.profiles.length; leftIndex += 1) {
const left = publishedCatalog.profiles[leftIndex];
for (const right of publishedCatalog.profiles.slice(leftIndex + 1)) {
if (left.hardware !== right.hardware || left.role !== right.role ||
!['ota', 'feature', 'mode', 'logging', 'install'].every(field => profilesOverlap(left, right, field))) continue;
assert.notStrictEqual(picker.firmwareProfileValue(left), picker.firmwareProfileValue(right),
'Overlapping selectors must not merge distinct firmware profile values: ' + left.target + ' / ' + right.target);
assert.notStrictEqual(picker.firmwareProfileLabel(left), picker.firmwareProfileLabel(right),
'Overlapping selectors must not show indistinguishable firmware labels: ' + left.target + ' / ' + right.target);
}
}
console.log('current T-LoRa profiles remain distinct and every overlapping release choice has a unique value and label');
// Partition expansion uses the packager's lookup and published utility assets,
// not hardware labels or guessed slugs (several historical names differ).
const migrationFamily = currentControls.familyTag;