diff --git a/docs/_javascript/preset_test.js b/docs/_javascript/preset_test.js index bf5361d13..47db346ae 100644 --- a/docs/_javascript/preset_test.js +++ b/docs/_javascript/preset_test.js @@ -131,6 +131,13 @@ } } + function hasPresetParameters(search) { + const params = new URLSearchParams(search || ""); + return Object.keys(DEFAULTS).some(function (key) { + return params.has(key); + }); + } + function supportedTimeZones(selectedTimeZone) { const zones = new Set(["UTC"]); if (typeof Intl.supportedValuesOf === "function") { @@ -723,52 +730,63 @@ } function init(root) { + const search = global.location ? global.location.search : ""; + let showTest = hasPresetParameters(search); let config; try { config = configFromSearch( - global.location ? global.location.search : "", + search, browserTimeZone() ); } catch (error) { const box = root.querySelector('[data-role="config-error"]'); box.hidden = false; box.textContent = "Invalid test URL: " + error.message; - root.querySelector('[data-role="content"]').hidden = true; - return; + config = configFromSearch("", browserTimeZone()); + showTest = false; } - setPageText('[data-role="preset-test-page-title"]', presetPageTitle(config)); - setPageText('[data-role="preset-test-page-summary"]', presetPageSummary(config)); - setText(root, '[data-role="preset-test-eyebrow"]', presetEyebrow(config)); - setText(root, '[data-field="freq-display"]', config.freq.toFixed(3)); - setText(root, '[data-field="bw-display"]', config.bwText); - setText(root, '[data-field="sf"]', String(config.sf)); - setText(root, '[data-field="cr"]', String(config.cr)); - setText(root, '[data-role="start-zoned"]', formatZoned(config.startMs, config.tz)); - setText(root, '[data-role="end-zoned"]', formatZoned(config.endMs, config.tz)); - setText(root, '[data-role="display-zone"]', config.tz); - setText( - root, - '[data-role="epoch-range"]', - config.startEpoch + " → " + config.endEpoch - ); - setText( - root, - '[data-role="window-summary"]', - "A " + formatCountdown(config.endMs - config.startMs) + - " window. Saved primary settings return automatically at the end." - ); + const testContent = root.querySelector('[data-role="test-content"]'); + if (testContent) testContent.hidden = !showTest; + const testFooter = root.querySelector('[data-role="test-content-footer"]'); + if (testFooter) testFooter.hidden = !showTest; + const builderDisclosure = root.querySelector('[data-role="url-builder-disclosure"]'); + if (builderDisclosure) builderDisclosure.open = !showTest; - const staticCommands = commandsFor(config, config.startMs); - setCommand(root, "primary-scheduled", staticCommands.primaryScheduled); - setCommand(root, "companion-scheduled", staticCommands.companionScheduled); - setCommand(root, "stock-cancel-during", staticCommands.stockCancelDuring); - setCommand(root, "stock-leave-30", staticCommands.stockLeaveIn30); - setCommand(root, "primary-cancel", staticCommands.primaryCancel); - setCommand(root, "companion-cancel-before", staticCommands.companionCancelBefore); - setCommand(root, "companion-cancel-during", staticCommands.companionCancelDuring); - setCommand(root, "companion-leave-30", staticCommands.companionLeaveIn30); - setCommand(root, "reset-clock", CLOCK_RESET_COMMAND); + if (showTest) { + setPageText('[data-role="preset-test-page-title"]', presetPageTitle(config)); + setPageText('[data-role="preset-test-page-summary"]', presetPageSummary(config)); + setText(root, '[data-role="preset-test-eyebrow"]', presetEyebrow(config)); + setText(root, '[data-field="freq-display"]', config.freq.toFixed(3)); + setText(root, '[data-field="bw-display"]', config.bwText); + setText(root, '[data-field="sf"]', String(config.sf)); + setText(root, '[data-field="cr"]', String(config.cr)); + setText(root, '[data-role="start-zoned"]', formatZoned(config.startMs, config.tz)); + setText(root, '[data-role="end-zoned"]', formatZoned(config.endMs, config.tz)); + setText(root, '[data-role="display-zone"]', config.tz); + setText( + root, + '[data-role="epoch-range"]', + config.startEpoch + " → " + config.endEpoch + ); + setText( + root, + '[data-role="window-summary"]', + "A " + formatCountdown(config.endMs - config.startMs) + + " window. Saved primary settings return automatically at the end." + ); + + const staticCommands = commandsFor(config, config.startMs); + setCommand(root, "primary-scheduled", staticCommands.primaryScheduled); + setCommand(root, "companion-scheduled", staticCommands.companionScheduled); + setCommand(root, "stock-cancel-during", staticCommands.stockCancelDuring); + setCommand(root, "stock-leave-30", staticCommands.stockLeaveIn30); + setCommand(root, "primary-cancel", staticCommands.primaryCancel); + setCommand(root, "companion-cancel-before", staticCommands.companionCancelBefore); + setCommand(root, "companion-cancel-during", staticCommands.companionCancelDuring); + setCommand(root, "companion-leave-30", staticCommands.companionLeaveIn30); + setCommand(root, "reset-clock", CLOCK_RESET_COMMAND); + } const generator = root.querySelector('[data-role="url-generator"]'); if (generator) { @@ -846,6 +864,8 @@ }); }); + if (!showTest) return; + function render() { const nowMs = Date.now(); const phase = phaseAt(config, nowMs); @@ -956,6 +976,7 @@ presetEyebrow: presetEyebrow, validateTimeZone: validateTimeZone, browserTimeZone: browserTimeZone, + hasPresetParameters: hasPresetParameters, supportedTimeZones: supportedTimeZones, phaseAt: phaseAt, remainingMinutes: remainingMinutes, diff --git a/docs/preset_test.md b/docs/preset_test.md index 871054964..c0f72105f 100644 --- a/docs/preset_test.md +++ b/docs/preset_test.md @@ -1,19 +1,15 @@ -# Default temporary radio test · 910.1 MHz · September 21–23, 2026 +# Temporary radio test builder -Use this page to join the shared temporary radio test without overwriting the -node's saved primary radio settings. The -default window is Monday, September 21, 2026 at 5:00 PM through -Wednesday, September 23 at 5:00 PM Pacific time (48 hours), using -910.1 MHz, 500 kHz, SF8, CR7. +Choose a window and radio tuple to +create a shareable temporary-radio test URL. -The page reads the complete test definition from its URL. Change the query -parameters to reuse it for a different window, display time zone, or radio -tuple; no source edit is required. +Open a generated URL to see that test's status, instructions, and commands.
+ +
-
+
-

Build a link for another test

+

Build a temporary radio test URL

@@ -412,13 +410,17 @@ clock

+ +
- -Primary tempradioat scheduling is implemented by the Simple Repeater -role; room-server and sensor roles use their immediate tempradio -command during the setup window. The Companion scheduling commands require -current full-parser firmware, and -tempradio2 and tempradioat2 require a build with -dual-radio-profile support. If a node reports an unknown command, use only a -method that its installed firmware documents. diff --git a/mkdocs.yml b/mkdocs.yml index 856447573..edb9fdf65 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,4 +30,4 @@ extra_javascript: - _javascript/telemetry_decoder.js - _javascript/management_decoder.js - _javascript/filter_tool.js - - _javascript/preset_test.js?v=20260921-1 + - _javascript/preset_test.js?v=20260921-2 diff --git a/test/test_preset_test.js b/test/test_preset_test.js index e6d7bef4b..af644c977 100644 --- a/test/test_preset_test.js +++ b/test/test_preset_test.js @@ -11,11 +11,9 @@ assert.match(pageSource, /data-role="preset-test-eyebrow"/); assert.match(pageSource, /data-role="timezone-map"/); assert.match(pageSource, /type="hidden" name="tz"/); assert.doesNotMatch(pageSource, /