Simplify stock temporary radio test page

This commit is contained in:
mikecarper
2026-09-22 22:50:28 -07:00
parent 49ceda27ba
commit c9a7be1eef
5 changed files with 387 additions and 295 deletions
+10 -3
View File
@@ -902,7 +902,6 @@
);
const staticCommands = setScheduledCommands();
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);
@@ -1057,19 +1056,27 @@
root,
"companion-now",
phase === "before"
? "Available one hour before the test — use Option 2 to schedule now."
? "Available one hour before the test — schedule in advance below if supported."
: endedMessage
);
}
setCommand(
root,
"stock-cancel-during",
immediateOpen ? commands.stockCancelDuring
: phase === "before" ? "Available after you join the test." : "Test ended — no need to leave."
);
setCommandEnabled(root, "stock-now", immediateOpen);
setCommandEnabled(root, "stock-cancel-during", immediateOpen);
setCommandEnabled(root, "companion-now", immediateOpen);
setMaterialCommandCopyEnabled(root, "stock-now", immediateOpen);
setMaterialCommandCopyEnabled(root, "stock-cancel-during", immediateOpen);
setMaterialCommandCopyEnabled(root, "companion-now", immediateOpen);
setText(
root,
'[data-role="stock-now-note"]',
phase === "before" && !immediateOpen
? "Available one hour before the test; schedule it now with Option 2."
? "The join command unlocks one hour before the test."
: phase === "before"
? "The setup window is open; the timeout includes the hour before the official start."
: phase === "active"
+45
View File
@@ -139,6 +139,27 @@
margin-bottom: 0;
}
.preset-test-quick-command {
border: 1px solid var(--preset-border);
border-radius: 0.6rem;
margin-top: 1rem;
padding: 0.85rem 1rem 1rem;
background: color-mix(in srgb, var(--preset-cyan) 5%, var(--md-default-bg-color));
}
.preset-test-quick-command h3 {
margin: 0 0 0.4rem;
font-size: 1rem;
}
.preset-test-quick-command .preset-test-note + .preset-test-note {
margin-top: 0.4rem;
}
.preset-test-stock-leave {
margin: 1rem 0;
}
.preset-test-error {
color: var(--md-code-hl-number-color);
font-weight: 650;
@@ -175,6 +196,8 @@
padding-right: 0.5rem;
}
.preset-test-quick-command pre,
.preset-test-stock-leave pre,
.preset-test-card pre,
.preset-test > [data-role="content"] > section > pre {
margin: 0.8rem 0;
@@ -182,6 +205,8 @@
border-radius: 0.35rem;
}
.preset-test-quick-command pre code,
.preset-test-stock-leave pre code,
.preset-test-card pre code,
.preset-test > [data-role="content"] > section > pre code {
white-space: pre-wrap;
@@ -348,6 +373,26 @@
content: "Close generator";
}
.preset-test-stock-leave > summary::after {
content: "Show leave command";
}
.preset-test-stock-leave[open] > summary::after {
content: "Hide leave command";
}
.preset-test-keymind-disclosure > summary::after {
content: "Show advanced";
}
.preset-test-keymind-disclosure {
margin-top: 1.4rem;
}
.preset-test-keymind-disclosure[open] > summary::after {
content: "Hide advanced";
}
.preset-test-generator-disclosure-body {
padding: 0 1rem 1rem;
}
+294 -290
View File
@@ -3,8 +3,6 @@
<span data-role="preset-test-page-summary">Choose a window and radio tuple to
create a shareable temporary-radio test URL.</span>
Open a generated URL to see that test's status, instructions, and commands.
<div class="preset-test" data-preset-test>
<div class="preset-test-error" data-role="config-error" role="alert" hidden></div>
@@ -25,6 +23,17 @@ Open a generated URL to see that test's status, instructions, and commands.
<span class="preset-test-status" data-role="status" data-state="before">Scheduled</span>
</div>
<div class="preset-test-quick-command" aria-labelledby="stock-test-title">
<h3 id="stock-test-title">Stock firmware: copy this <code>tempradio</code> command</h3>
<pre><code data-command="stock-now"></code></pre>
<button type="button" data-copy-command="stock-now">Copy tempradio command</button>
<p class="preset-test-note" data-role="stock-now-note"></p>
<p class="preset-test-note">
Before sending it, run <code>get radio</code> and save the result.
No clock setup is needed. Your saved settings return automatically.
</p>
</div>
<div class="preset-test-clock" aria-live="polite">
<span class="preset-test-clock-label" data-role="countdown-label">Starts in</span>
<strong data-role="countdown">—</strong>
@@ -35,291 +44,17 @@ Open a generated URL to see that test's status, instructions, and commands.
<div><dt>Start</dt><dd data-role="start-zoned"></dd></div>
<div><dt>End</dt><dd data-role="end-zoned"></dd></div>
<div><dt>Display time zone</dt><dd data-role="display-zone"></dd></div>
<div><dt>Firmware epochs</dt><dd><code data-role="epoch-range"></code></dd></div>
</dl>
</section>
<section class="preset-test-warning" aria-labelledby="continuity-plan-title">
<h2 id="continuity-plan-title">Bridges and early-revert plan</h2>
<p>
The test will include bridges. If the Puget Sound area experiences a
widespread power outage or loss of cellular service, we will end the
test early. A stock node's one-minute temporary test profile then ends
on that node's own saved radio settings.
</p>
</section>
<section class="preset-test-warning" aria-labelledby="save-settings-title">
<h2 id="save-settings-title">Before changing a radio</h2>
<p>
Run <code>get radio</code> and record the saved primary tuple. Both
primary TempRadio methods return to that saved tuple; they do not store
a return tuple inside the temporary command. On a Companion, also run
<code>get radio2</code> and <code>get radio2.cross</code> so you can restore
any non-default secondary-profile setup.
</p>
</section>
<section aria-labelledby="clock-check-title">
<h2 id="clock-check-title">Check the node clock before scheduling</h2>
<p>
The primary <code>tempradioat</code> and Companion
<code>tempradioat2</code> schedulers use UTC Unix time. Run
<code>clock</code> on every node that will be scheduled and compare it
with the browser UTC time below. It should agree to within about a minute.
A wrong clock can start late, start immediately, or cause the schedule to
be rejected.
</p>
<div class="preset-test-clock-check">
<div>
<span>Browser UTC now</span>
<strong data-role="browser-utc">—</strong>
</div>
<div>
<span>Browser Unix time</span>
<strong data-role="browser-epoch">—</strong>
</div>
<details class="preset-test-generator-disclosure preset-test-stock-leave">
<summary><h2>Need to leave the test early?</h2></summary>
<div class="preset-test-generator-disclosure-body">
<p>Send this command to return to your saved radio settings in one minute:</p>
<pre><code data-command="stock-cancel-during"></code></pre>
<button type="button" data-copy-command="stock-cancel-during">Copy leave command</button>
</div>
<div class="preset-test-clock-override">
<label>
<span>Node clock reported by <code>clock</code> (UTC, optional)</span>
<input
type="text"
inputmode="text"
autocomplete="off"
spellcheck="false"
data-role="node-clock-input"
placeholder="02:42 22/9/2026 UTC"
>
<small>
Paste the node's <code>clock</code> reply immediately after it arrives.
The firmware form <code>02:42 - 22/9/2026 UTC</code> is also accepted.
Leave this blank to keep the normal UTC schedule epochs unchanged.
</small>
</label>
<button type="button" data-action="apply-node-clock">Apply clock conversion</button>
</div>
<p class="preset-test-clock-status" data-role="node-clock-status" aria-live="polite">
No node-clock correction is applied. Scheduled commands use the normal UTC epochs.
</p>
<p class="preset-test-note">
This changes only the absolute <code>tempradioat</code> and
<code>tempradioat2</code> epochs shown on this page; it does not issue a
clock-setting command. The correction is for this node and this open page
only: it is never added to generated URLs, and is cleared on reload. If
the node clock later syncs or jumps, delete and queue the schedule again.
</p>
<div class="preset-test-grid preset-test-grid--clock">
<article class="preset-test-card">
<h3>Remote admin session</h3>
<p>
In a MeshCore client that supplies the sender timestamp, sync and
then verify:
</p>
<pre><code>clock sync
clock</code></pre>
</article>
<article class="preset-test-card">
<h3>Local USB or browser console</h3>
<p>Copy this fresh, run it immediately, and then verify with <code>clock</code>:</p>
<pre><code data-command="set-clock">time 0
clock</code></pre>
<button type="button" data-copy-command="set-clock">Copy current clock command</button>
</article>
<article class="preset-test-card preset-test-card--wide">
<h3>If the clock is ahead and cannot move backward</h3>
<p>
If setting the correct time returns <code>ERR: clock cannot go backwards</code>,
run the command below. It resets the clock to an older fallback date and
reboots the node immediately, so it sends no reply. After the node reconnects,
copy and run the fresh local clock command above, verify with <code>clock</code>,
and then add the Companion <code>tempradioat2</code> schedule again. The
reboot clears any pending scheduled entries.
</p>
<p>
If resetting or correcting this remote node's clock is not safe, leave
it unchanged and use the optional node-clock conversion above instead.
</p>
<pre><code data-command="reset-clock">clkreboot</code></pre>
<button type="button" data-copy-command="reset-clock">Copy clock reset command</button>
</article>
</div>
<p class="preset-test-note">
The Companion schedule must be queued while both its start and end are
in the future and within the firmware's roughly 24-day scheduling
horizon. Scheduled entries are held in RAM and disappear if the node
reboots.
</p>
</section>
<section aria-labelledby="join-now-title">
<h2 id="join-now-title">Option 1: switch during the setup window</h2>
<p>
The immediate commands become available one hour before the official
start. Their timeout shrinks so every node still returns at the same end
time. Before that setup window, the command text and copy controls stay
unavailable.
</p>
<div class="preset-test-grid">
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Stock repeater, room server, or sensor</h3>
<span>Primary radio</span>
</div>
<pre><code data-command="stock-now"></code></pre>
<button type="button" data-copy-command="stock-now">Copy command</button>
<p class="preset-test-note" data-role="stock-now-note"></p>
</article>
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Companion using both frequencies</h3>
<span>Dual profile</span>
</div>
<pre><code data-command="companion-now"></code></pre>
<button type="button" data-copy-command="companion-now">Copy commands</button>
<p class="preset-test-note">
<code>rxtx</code> permits transmission on the second profile;
<code>radio2.cross on</code> copies ordinary Companion traffic across
the primary and temporary profiles.
</p>
</article>
</div>
</section>
<section aria-labelledby="schedule-title">
<h2 id="schedule-title">Option 2: schedule a supported node in advance</h2>
<p>
A Simple Repeater build can schedule its primary radio with
<code>tempradioat</code>. A Companion can instead schedule its second
profile with <code>tempradioat2</code>. Both commands use the exact UTC
epochs below, switch at the common start, and restore saved settings at
the common end. A node-clock conversion above replaces only those command
epochs with its per-node translated values. Room-server and sensor roles
use Option 1 instead.
</p>
<div class="preset-test-grid">
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Room server or sensor</h3>
<span>No advance schedule</span>
</div>
<p class="preset-test-note">
Use the immediate <code>tempradio</code> command in Option 1 after
its setup window opens.
</p>
</article>
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Simple Repeater primary radio</h3>
<span>Primary schedule</span>
</div>
<pre><code data-command="primary-scheduled"></code></pre>
<button type="button" data-copy-command="primary-scheduled">Copy schedule commands</button>
<p class="preset-test-note">
This is available only on a Simple Repeater build with primary
scheduled-radio support. It changes the primary tuple at the start
and restores the saved tuple at the end. If <code>get tempradioat</code>
returns <code>Error: unsupported</code>, use Option 1 instead.
</p>
</article>
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Companion using both frequencies</h3>
<span>Dual-profile schedule</span>
</div>
<pre><code data-command="companion-scheduled"></code></pre>
<button type="button" data-copy-command="companion-scheduled">Copy schedule commands</button>
<p class="preset-test-note">
Crossing is saved independently and remains <code>on</code> after the
temporary second profile ends. Restore its previous value after the
test; <code>auto</code> is the normal default.
</p>
</article>
</div>
</section>
<section aria-labelledby="cancel-title">
<h2 id="cancel-title">Cancel or leave the test</h2>
<p>
A stock node can leave an active test as shown below. For a primary or
Companion schedule, the <code>all</code> form removes every temporary
schedule in that family. To preserve another schedule, first run the
corresponding <code>get</code> command and replace <code>all</code> with
its displayed entry number.
</p>
<div class="preset-test-cancel-grid">
<article class="preset-test-card">
<h3>Stock · while it is running</h3>
<h4>Leave now · stock compatible</h4>
<pre><code data-command="stock-cancel-during"></code></pre>
<button type="button" data-copy-command="stock-cancel-during">Copy stock leave command</button>
<p class="preset-test-note">
Stock MeshCore has no <code>normalradio</code> command. This starts a
one-minute temporary session on this page's test profile without
saving over the node's configuration. When that minute ends, the node
restores whatever primary radio settings it had saved.
</p>
<h4>Leave in 30 minutes</h4>
<pre><code data-command="stock-leave-30"></code></pre>
<button type="button" data-copy-command="stock-leave-30">Copy 30-minute command</button>
<p class="preset-test-note">
This replaces the current temporary timeout with 30 minutes, then
restores the saved primary tuple automatically.
</p>
</article>
<article class="preset-test-card">
<h3>Simple Repeater · scheduled primary radio</h3>
<pre><code data-command="primary-cancel"></code></pre>
<button type="button" data-copy-command="primary-cancel">Copy cancel commands</button>
<p class="preset-test-note">
Before the start, this removes the queued temporary schedule. While
it is running, it also restores the saved primary tuple after the
command reply drains.
</p>
</article>
<article class="preset-test-card">
<h3>Companion · before it starts</h3>
<pre><code data-command="companion-cancel-before"></code></pre>
<button type="button" data-copy-command="companion-cancel-before">Copy cancel commands</button>
</article>
<article class="preset-test-card">
<h3>Companion · while it is running</h3>
<h4>Leave now</h4>
<pre><code data-command="companion-cancel-during"></code></pre>
<button type="button" data-copy-command="companion-cancel-during">Copy restore commands</button>
<p class="preset-test-note">
If <code>get radio2.cross</code> was not <code>auto</code> before the
test, restore that recorded value instead.
</p>
<h4>Leave in 30 minutes</h4>
<pre><code data-command="companion-leave-30"></code></pre>
<button type="button" data-copy-command="companion-leave-30">Copy 30-minute commands</button>
<p class="preset-test-note">
These commands clear any absolute second-profile schedule and give
the test profile a fresh 30-minute lease. The saved second profile
returns at expiry, but crossing remains <code>on</code>; afterward,
restore the value you recorded before the test (<code>auto</code> is
the normal default).
</p>
</article>
</div>
</section>
</details>
</div>
@@ -449,15 +184,284 @@ clock</code></pre>
</section>
<div data-role="test-content-footer" hidden>
<details class="preset-test-generator-disclosure preset-test-keymind-disclosure"
data-role="keymind-disclosure">
<summary><h2>KeyMind firmware and advanced commands</h2></summary>
<div class="preset-test-generator-disclosure-body">
<p>
Primary <code>tempradioat</code> scheduling is implemented by the Simple
Repeater role; room-server and sensor roles use their immediate
<code>tempradio</code> command during the setup window. The Companion
scheduling commands require current full-parser firmware, and
<code>tempradio2</code> and <code>tempradioat2</code> require a build with
dual-radio-profile support. If a node reports an unknown command, use only
a method that its installed firmware documents.
Use these only if your firmware supports the listed commands or you need
dual-radio operation, scheduling, or clock correction.
</p>
<p>Firmware schedule epochs: <code data-role="epoch-range"></code></p>
<section class="preset-test-warning" aria-labelledby="continuity-plan-title">
<h2 id="continuity-plan-title">Bridges and early-revert plan</h2>
<p>
The test will include bridges. If the Puget Sound area experiences a
widespread power outage or loss of cellular service, we will end the
test early. A stock node's one-minute temporary test profile then ends
on that node's own saved radio settings.
</p>
</section>
<section class="preset-test-warning" aria-labelledby="save-settings-title">
<h2 id="save-settings-title">Before changing a radio</h2>
<p>
Run <code>get radio</code> and record the saved primary tuple. Both
primary TempRadio methods return to that saved tuple; they do not store
a return tuple inside the temporary command. On a Companion, also run
<code>get radio2</code> and <code>get radio2.cross</code> so you can restore
any non-default secondary-profile setup.
</p>
</section>
<section aria-labelledby="clock-check-title">
<h2 id="clock-check-title">Check the node clock before scheduling</h2>
<p>
The primary <code>tempradioat</code> and Companion
<code>tempradioat2</code> schedulers use UTC Unix time. Run
<code>clock</code> on every node that will be scheduled and compare it
with the browser UTC time below. It should agree to within about a minute.
A wrong clock can start late, start immediately, or cause the schedule to
be rejected.
</p>
<div class="preset-test-clock-check">
<div>
<span>Browser UTC now</span>
<strong data-role="browser-utc">—</strong>
</div>
<div>
<span>Browser Unix time</span>
<strong data-role="browser-epoch">—</strong>
</div>
</div>
<div class="preset-test-clock-override">
<label>
<span>Node clock reported by <code>clock</code> (UTC, optional)</span>
<input
type="text"
inputmode="text"
autocomplete="off"
spellcheck="false"
data-role="node-clock-input"
placeholder="02:42 22/9/2026 UTC"
>
<small>
Paste the node's <code>clock</code> reply immediately after it arrives.
The firmware form <code>02:42 - 22/9/2026 UTC</code> is also accepted.
Leave this blank to keep the normal UTC schedule epochs unchanged.
</small>
</label>
<button type="button" data-action="apply-node-clock">Apply clock conversion</button>
</div>
<p class="preset-test-clock-status" data-role="node-clock-status" aria-live="polite">
No node-clock correction is applied. Scheduled commands use the normal UTC epochs.
</p>
<p class="preset-test-note">
This changes only the absolute <code>tempradioat</code> and
<code>tempradioat2</code> epochs shown on this page; it does not issue a
clock-setting command. The correction is for this node and this open page
only: it is never added to generated URLs, and is cleared on reload. If
the node clock later syncs or jumps, delete and queue the schedule again.
</p>
<div class="preset-test-grid preset-test-grid--clock">
<article class="preset-test-card">
<h3>Remote admin session</h3>
<p>
In a MeshCore client that supplies the sender timestamp, sync and
then verify:
</p>
<pre><code>clock sync
clock</code></pre>
</article>
<article class="preset-test-card">
<h3>Local USB or browser console</h3>
<p>Copy this fresh, run it immediately, and then verify with <code>clock</code>:</p>
<pre><code data-command="set-clock">time 0
clock</code></pre>
<button type="button" data-copy-command="set-clock">Copy current clock command</button>
</article>
<article class="preset-test-card preset-test-card--wide">
<h3>If the clock is ahead and cannot move backward</h3>
<p>
If setting the correct time returns <code>ERR: clock cannot go backwards</code>,
run the command below. It resets the clock to an older fallback date and
reboots the node immediately, so it sends no reply. After the node reconnects,
copy and run the fresh local clock command above, verify with <code>clock</code>,
and then add the Companion <code>tempradioat2</code> schedule again. The
reboot clears any pending scheduled entries.
</p>
<p>
If resetting or correcting this remote node's clock is not safe, leave
it unchanged and use the optional node-clock conversion above instead.
</p>
<pre><code data-command="reset-clock">clkreboot</code></pre>
<button type="button" data-copy-command="reset-clock">Copy clock reset command</button>
</article>
</div>
<p class="preset-test-note">
The Companion schedule must be queued while both its start and end are
in the future and within the firmware's roughly 24-day scheduling
horizon. Scheduled entries are held in RAM and disappear if the node
reboots.
</p>
</section>
<section aria-labelledby="join-now-title">
<h2 id="join-now-title">Companion: use both frequencies</h2>
<p>
These commands become available one hour before the official start.
Their timeout shrinks so the Companion returns at the same end time.
</p>
<div class="preset-test-grid">
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Companion using both frequencies</h3>
<span>Dual profile</span>
</div>
<pre><code data-command="companion-now"></code></pre>
<button type="button" data-copy-command="companion-now">Copy commands</button>
<p class="preset-test-note">
<code>rxtx</code> permits transmission on the second profile;
<code>radio2.cross on</code> copies ordinary Companion traffic across
the primary and temporary profiles.
</p>
</article>
</div>
</section>
<section aria-labelledby="schedule-title">
<h2 id="schedule-title">Schedule a supported node in advance</h2>
<p>
A Simple Repeater build can schedule its primary radio with
<code>tempradioat</code>. A Companion can instead schedule its second
profile with <code>tempradioat2</code>. Both commands use the exact UTC
epochs below, switch at the common start, and restore saved settings at
the common end. A node-clock conversion above replaces only those command
epochs with its per-node translated values. Room-server and sensor roles
use the stock join command above instead.
</p>
<div class="preset-test-grid">
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Room server or sensor</h3>
<span>No advance schedule</span>
</div>
<p class="preset-test-note">
Use the stock <code>tempradio</code> command above after
its setup window opens.
</p>
</article>
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Simple Repeater primary radio</h3>
<span>Primary schedule</span>
</div>
<pre><code data-command="primary-scheduled"></code></pre>
<button type="button" data-copy-command="primary-scheduled">Copy schedule commands</button>
<p class="preset-test-note">
This is available only on a Simple Repeater build with primary
scheduled-radio support. It changes the primary tuple at the start
and restores the saved tuple at the end. If <code>get tempradioat</code>
returns <code>Error: unsupported</code>, use the stock join command
above instead.
</p>
</article>
<article class="preset-test-card">
<div class="preset-test-card-heading">
<h3>Companion using both frequencies</h3>
<span>Dual-profile schedule</span>
</div>
<pre><code data-command="companion-scheduled"></code></pre>
<button type="button" data-copy-command="companion-scheduled">Copy schedule commands</button>
<p class="preset-test-note">
Crossing is saved independently and remains <code>on</code> after the
temporary second profile ends. Restore its previous value after the
test; <code>auto</code> is the normal default.
</p>
</article>
</div>
</section>
<section aria-labelledby="cancel-title">
<h2 id="cancel-title">Cancel a schedule or adjust the timeout</h2>
<p>
For a primary or Companion schedule, the <code>all</code> form removes every temporary
schedule in that family. To preserve another schedule, first run the
corresponding <code>get</code> command and replace <code>all</code> with
its displayed entry number.
</p>
<div class="preset-test-cancel-grid">
<article class="preset-test-card">
<h3>Stock: leave in 30 minutes</h3>
<pre><code data-command="stock-leave-30"></code></pre>
<button type="button" data-copy-command="stock-leave-30">Copy 30-minute command</button>
<p class="preset-test-note">
This replaces the current temporary timeout with 30 minutes, then
restores the saved primary tuple automatically.
</p>
</article>
<article class="preset-test-card">
<h3>Simple Repeater · scheduled primary radio</h3>
<pre><code data-command="primary-cancel"></code></pre>
<button type="button" data-copy-command="primary-cancel">Copy cancel commands</button>
<p class="preset-test-note">
Before the start, this removes the queued temporary schedule. While
it is running, it also restores the saved primary tuple after the
command reply drains.
</p>
</article>
<article class="preset-test-card">
<h3>Companion · before it starts</h3>
<pre><code data-command="companion-cancel-before"></code></pre>
<button type="button" data-copy-command="companion-cancel-before">Copy cancel commands</button>
</article>
<article class="preset-test-card">
<h3>Companion · while it is running</h3>
<h4>Leave now</h4>
<pre><code data-command="companion-cancel-during"></code></pre>
<button type="button" data-copy-command="companion-cancel-during">Copy restore commands</button>
<p class="preset-test-note">
If <code>get radio2.cross</code> was not <code>auto</code> before the
test, restore that recorded value instead.
</p>
<h4>Leave in 30 minutes</h4>
<pre><code data-command="companion-leave-30"></code></pre>
<button type="button" data-copy-command="companion-leave-30">Copy 30-minute commands</button>
<p class="preset-test-note">
These commands clear any absolute second-profile schedule and give
the test profile a fresh 30-minute lease. The saved second profile
returns at expiry, but crossing remains <code>on</code>; afterward,
restore the value you recorded before the test (<code>auto</code> is
the normal default).
</p>
</article>
</div>
</section>
<p class="preset-test-note">
Primary scheduling requires a Simple Repeater build. Companion
<code>tempradio2</code> and <code>tempradioat2</code> require dual-profile
firmware. If a command is unknown, use only the method your installed
firmware supports.
</p>
</div>
</details>
</div>
</div>
</div>
+2 -2
View File
@@ -22,7 +22,7 @@ extra_css:
- _stylesheets/telemetry_decoder.css
- _stylesheets/management_decoder.css
- _stylesheets/filter_tool.css
- _stylesheets/preset_test.css?v=20260921-1
- _stylesheets/preset_test.css?v=20260922-1
extra_javascript:
- https://unpkg.com/leaflet@1.9.4/dist/leaflet.js
@@ -30,4 +30,4 @@ extra_javascript:
- _javascript/telemetry_decoder.js
- _javascript/management_decoder.js
- _javascript/filter_tool.js
- _javascript/preset_test.js?v=20260921-3
- _javascript/preset_test.js?v=20260922-1
+36
View File
@@ -14,6 +14,38 @@ assert.doesNotMatch(pageSource, /<select name="tz"/);
assert.match(pageSource, /data-role="test-content" hidden/);
assert.match(pageSource, /data-role="test-content-footer" hidden/);
assert.match(pageSource, /data-role="url-builder-disclosure" open/);
assert.match(pageSource, /class="preset-test-quick-command"/);
assert.match(pageSource, /class="preset-test-generator-disclosure preset-test-stock-leave"/);
assert.match(pageSource, /data-role="keymind-disclosure"/);
assert.match(pageSource, /<details class="preset-test-generator-disclosure preset-test-keymind-disclosure"\s+data-role="keymind-disclosure">/);
const keymindStart = pageSource.indexOf('data-role="keymind-disclosure"');
const keymindEnd = pageSource.indexOf("</details>", keymindStart);
const keymindContent = pageSource.slice(keymindStart, keymindEnd);
assert(pageSource.indexOf('id="stock-test-title"') < keymindStart);
assert(pageSource.indexOf('data-command="stock-now"') <
pageSource.indexOf('data-role="countdown"'));
assert(pageSource.indexOf('data-command="stock-now"') <
pageSource.indexOf('data-role="url-builder-disclosure"'));
assert(pageSource.indexOf('data-role="url-builder-disclosure"') <
pageSource.indexOf('data-role="test-content-footer"'));
assert(pageSource.indexOf('data-role="test-content-footer"') < keymindStart);
assert.strictEqual((pageSource.match(/data-command="stock-now"/g) || []).length, 1);
assert.strictEqual((pageSource.match(/data-command="stock-cancel-during"/g) || []).length, 1);
assert.doesNotMatch(keymindContent, /data-command="stock-now"/);
for (const name of ["set-clock", "reset-clock", "companion-now", "primary-scheduled",
"companion-scheduled", "primary-cancel", "companion-cancel-before",
"companion-cancel-during"]) {
assert.match(keymindContent, new RegExp('data-command="' + name + '"'));
}
const openTags = [];
for (const match of pageSource.matchAll(/<\/?(div|details|section|article|summary)\b[^>]*>/g)) {
if (match[0][1] === "/") {
assert.strictEqual(openTags.pop(), match[1], "nested page HTML must stay balanced");
} else {
openTags.push(match[1]);
}
}
assert.deepStrictEqual(openTags, []);
assert.match(pageSource, /data-role="node-clock-input"/);
assert.match(pageSource, /placeholder="02:42 22\/9\/2026 UTC"/);
assert.match(pageSource, /data-action="apply-node-clock"/);
@@ -98,6 +130,10 @@ assert.strictEqual(tool.presetPageTitle(requestedLink), "Temporary radio test ·
assert.match(tool.presetPageSummary(requestedLink), /911\.3 MHz, 500 kHz, SF8, CR7, 22 dBm/);
assert.doesNotMatch(tool.presetPageSummary(requestedLink), /910\.1 MHz/);
assert.strictEqual(tool.presetEyebrow(requestedLink), "MeshCore · 48-hour temporary preset test");
assert.strictEqual(
tool.commandsFor(requestedLink, requestedLink.startMs).stockNow,
"tempradio 911.3,500,8,7,2880"
);
assert.strictEqual(
tool.commandsFor(requestedLink, requestedLink.startMs).primaryScheduled,
"set tempradioat 911.3,500,8,7,1790035200,1790208000\nget tempradioat"