19 Commits
Author SHA1 Message Date
Kaj SchittecatandClaude Opus 5.5 09c4c9e193 test: wada.ui.input max_len and wada.sys.random in the host build and harness
web/scripts/apps/inputlen.lua + web/scripts/lua-input.wada run against the
real LuaAppHost.cpp in the web host build: caps().input_len is true,
random(6) stays in 1..6 with every value seen, random(1, 1) is 1,
random(5, 3) is in 3..5, random(1) is 1, random() is a raw word; 250
characters typed into wada.ui.input(..., 300) come back whole on Enter,
100 typed with no max_len come back as 63; the full 32-bit range returns
integers.

The Lua harness mock now mirrors the current host: sys.random with the
new ranges (deterministic xorshift32), caps().input / input_len, and
wada.ui.input with its argument checks, the 63 default, the 1..2000
clamp and one prompt at a time, answered by answer_input(). Scenarios
inputlen_random and inputlen_input run the same app; every store app's
scenarios still pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 13:58:23 +01:00
Sebastian Rutofski fcea1c1510 test(harness): cover the wardrive start/stop button
The button names what the next press will do, and relabelling in place is only
safe on firmware that carries the button:set fix. Add wardrive_start_stop
(Start/Stop toggles under caps().panel) and wardrive_start_stop_old_firmware
(the label stays put without it), both scoped to the 1.3 path so 1.1 and 1.2
keep running the utf8 scenario alone.

Red until the fix on top: wardrive_start_stop expects "Stop" while the survey
runs, which the app does not do yet.

Assisted-by: OpenCode:deepseek-v4.1-flash
Signed-off-by: Sebastian Rutofski <kontakt@sebastian-rutofski.de>
2026-10-23 11:28:15 +02:00
Kaj Schittecat 7d0bf8b975 Merge PR #607 (samuelcoustet): Trip Odometer 1.1
Harness: #648 and #607 each declared the map mock's map_obj/map_centers
locals; the second declaration shadowed the first, so reset_world() and the
scenarios after it would never see the map the mock created. One declaration
is kept. tripodometer, protreck and gpscompass harness runs: ALL OK.
2026-10-21 12:41:49 +02:00
SamandClaude Sonnet 4.6 2929467a75 fix(apps): ProTreck 1.2 — address reviewer feedback on PR #648
apps.json: one protreck row, ver=1.2, requires=sdk_ext (no seed: false)

protreck.lua:
- get_heading(): compute from raw compass x/y via atan(c.y,-c.x); remove
  c.hdg (no such field) and GPS course fallback (M9-only)
- tap detection: record press={x,y} on "down", act on "up" only when
  distance <= 12 px — swipes no longer trigger waypoint/chrono/timer/alarm
- alarm clock: use sys.datetime() (local time) instead of now_unix()+decompose()
- wada.timer.after: schedule_alarm() fires alarms while screen sleeps, rescheduled daily
- key events: left/right = tab nav, up/down mirror swipe actions
- canvas capped at 480×480 (Tanmatsu compatibility)

harness.lua:
- timer.after mock added
- protreck_tdeck fixture: h=240→196 (T-Deck actual resolution)
- protreck_chrono / protreck_timer / protreck_compass: send down+up tap pairs
- protreck_compass: compass mock returns raw {x,y,z} (not {hdg})
- protreck_deniv: alt_seq reordered so step 9 (first on_tick sample) is the
  max value, matching the GPS call order through draw_status_bar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-15 19:04:14 +02:00
SamandClaude Sonnet 4.6 393eade8eb feat(apps): Trip Odometer 1.1 — return-to-home, avg speed, min altitude
Builds on 1.0 (already in main). New in 1.1:

- Return to home: records the position of the first GPS fix after Start
  and shows straight-line distance + cardinal bearing back to that point
  each poll. Home is persisted in wada.store (trip_home_lat_e6/lon_e6)
  so reopening mid-trip keeps the original departure point. Clears on Reset.
- Average speed: total_km / elapsed_s × 3600, live tile beside max speed.
- Min altitude: tracked alongside current altitude, persisted via
  wada.store ("trip_minalt"). Clears on Reset.
- Speed derivation: when GPS speed_kmh is absent (non-M9 boards) speed is
  derived from successive fixes; avg speed stays ≤ max speed by construction.
- Layout proportional to screen height: rows fill the space between the speed
  block and the button bar — no overflow on T-Deck (320×240) or V4 (240×276).
- requires: sdk_ext (sys.gps is an ext-SDK function; errors on 2 MB V4 otherwise).

Harness: realistic fixtures (step × 0.0002° ≈ 22 m/2 s ≈ 40 km/h), home
persistence asserted, avg ≤ max by construction in all non-M9 scenarios.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-15 18:55:28 +02:00
SamandClaude Sonnet 4.6 f098714ede feat(apps): ProTreck 1.2 — compass, 3 alarms, déniv, status bar
New version in deploy/apps/protreck/1.2/ (1.0 directory untouched).

- 5 tabs: ASTRO · CMPAS · CHRONO · TIMER · ALTI (was 4)
- Permanent status bar: local time (UTC offset from GPS longitude),
  GPS sats, battery % — shown on all tabs
- ASTRO: 3 alarms A1/A2/A3; short tap = cycle slot, long press ≥500ms
  = toggle on/off; swipe up/down ±15 min on selected alarm;
  civil twilight via generalised sun_utc_dep() formula
- CMPAS (new tab): compass rose with cv:line rotating marks;
  sys.compass() or GPS course fallback; tap = save/clear waypoint;
  bearing + distance shown on rose
- ALTI: cumulative déniv+/− (0.5m noise threshold), climb rate m/h;
  GPS sampling active on all tabs
- Harness: 10 scenarios ALL OK, worst tick ~13k / budget 100k

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-11 16:12:45 +02:00
Kaj SchittecatandClaude Opus 5 66e1f37d2c harness: model the filesystem, and stop apps running other apps' tests
Two things made a full harness run untrustworthy, and together they produced a
false accusation against a community app.

wada.fs was stubs. append threw the data away, remove always said true, and
read, write and list were not there at all — although the firmware registers
all five (LuaAppHost.cpp). Any app that writes a file and reads it back
therefore failed here while working perfectly on device. wardrive 1.2 is
exactly that app: it errors on a nil fs.read at its first log flush, long
before it renders anything, and the emoji scenario then reported the repeater
name as unrendered. Read as "1.2 regressed the beta_70 emoji fix", which it
did not. It passes untouched once fs is real.

wada.fs is now an in-memory filesystem following LuaAppHost's semantics: read
returns the data plus the file's TOTAL size so a caller can window a growing
log, an offset at or past the end gives an empty string rather than nil, a
missing file gives nil, and list is sorted so a hash order cannot make a test
flaky.

The second: the gpscompass scenario list was acting as the dispatch default,
so an app with no branch of its own was run against tests written for a
different app. Six store apps have no branch (2048, airtime, monitor, nearby,
sdktest, snake), so a full sweep came back with six red apps as its normal
state — and a real regression would not have stood out against that. Those
apps now say they have no scenarios and run none. They are untested, which is
worth saying out loud, but that is not the same as broken.

A sweep of every store app is now 8 apps passing their own scenarios, 6
reported untested, 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-04 12:42:12 +02:00
Kaj Schittecat 3171f5adea Merge PR #586 from samuelcoustet: Breakout and Trip Odometer Lua apps
Stacked on #585 and merged after it, as the PR asks. Two conflicts, both from
the stack base moving:

  apps.json: unioned by id, so all fourteen apps survive.
  harness.lua: the additive blocks came from #586. Where both sides edited the
  same code, #585's is newer and wins: its ping scenarios pass cfg.gps as a
  FUNCTION, which is what the harness calls (cfg.gps()); #586 still passed the
  table and would have failed. Taking #586 there would have broken ping.

  Then restored the two dispatch branches my first resolution dropped. The
  breakout and tripodometer scenarios existed but nothing ran them, so both
  apps silently fell through to the gpscompass scenarios and "failed".

All five new apps pass their own scenarios, and so do gpscompass and sdscan.
2026-10-03 17:24:15 +02:00
SamandClaude Sonnet 4.6 06f9c6e549 feat(apps): ProTreck 1.0 — outdoor watch Lua app (Astro/Chrono/Timer/Alti)
Sun/Moon rise+set from GPS lat/lon + date (Spencer 1971), moon phase 0-29.
Chronometer with lap times, countdown timer with alarm beep and preset
cycling, altimeter with GPS alt_m, min/max persistence and trend graph.
Swipe left/right to switch tabs. 4 tabs fit any screen size.

test(harness): add 7 ProTreck scenarios (tdeck, v4, no_gps, chrono,
timer, alti, cost). ALL OK 7/7.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-02 22:52:51 +02:00
rootandClaude Sonnet 4.6 7254b9cca6 feat(apps): Ping 1.0 — mesh DM ping with GNSS position and RTT
Send a DM to any contact, measure round-trip time.  Both devices
exchange their GPS coordinates; the result shows RTT, distance and
cardinal bearing when both have a fix.  The app also auto-replies to
incoming pings so two devices running Ping can time each other.

Harness additions: send_dm mock, wada.geo (haversine), deliver_dm
helper, and 6 scenarios (tdeck, v4, no_contacts, round_trip,
auto_reply, cost) — all pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-02 22:50:22 +02:00
SamandClaude Sonnet 4.6 9b7ca27747 feat(apps): Trip Odometer 1.0 — GPS trip computer as a Lua app
Tracks total distance (Haversine accumulation), elapsed time, current
speed, max speed, and altitude from live GPS fixes.  Start/Stop button
pauses accumulation; Reset clears the trip.  The current trip persists
across app restarts via wada.store.

Also adds 6 harness scenarios (no-fix, T-Deck, moving, persist, reset,
cost) and registers the app in apps.json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-02 22:41:14 +02:00
SamandClaude Sonnet 4.6 ec57effffb feat(apps): Breakout 1.0 — classic brick-breaker as a Lua app
Ball + paddle + 8×5 brick grid. Swipe left/right (or arrow keys) to
move the paddle; swipe up / Enter / Space to launch. Angle on bounce
varies by paddle-hit position. Speed increases each level; high score
persists via wada.store.

Adds 6 harness scenarios (tdeck, v4, keyboard, logic, hiscore, cost).
Worst tick ~8k instructions, 8% of the 100k budget.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-02 22:37:57 +02:00
SamandClaude Sonnet 4.6 44f5dae4eb test(harness): add Tetris scenarios — tdeck, v4, keyboard, logic, hiscore, cost
Detects /tetris/ in the app path and routes to 6 dedicated scenarios:
- tetris_tdeck/v4: open + swipe input on touch screens
- tetris_keyboard: arrow + enter input on keyboard boards
- tetris_logic: hard-drop to game-over, verify restart resets timer to 700 ms
- tetris_hiscore: verify high score is stored as a number after game-over
- tetris_cost: 100 ticks, worst ~6000 instructions (budget 100000)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-02 22:21:44 +02:00
Kaj SchittecatandClaude Opus 5 80f6aa97ea apps: SD Scan 1.1, buttons above the findings list
An infected M9 card turned up with 373 findings. Every list row is a focus
stop on a keyboard board, and the firmware's key navigation collects at most
160 of them per screen, so in 1.0 the Remove all and Scan again buttons below
the list could not be reached on an M9 at all.

1.1 puts the buttons above the list, first in focus order, on the results
and failed-removal screens, and shows at most 60 rows plus an "and N more"
row (the title carries the full count). App-only: no firmware change.

Harness: a 373-finding scenario, and a check on every list screen that the
buttons come first and the focus stops stay under 160. It fails on 1.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 09:13:43 +02:00
Kaj SchittecatandClaude Opus 5 6868911f59 touch: SD Scan, find and remove Windows malware on the SD card
Some ThinkNode M9 cards shipped with a dormant Windows worm (Elecrow
security advisory, September 2026). An infected card seen since carries
autorun.inf in the root, launching xlfqf.pif on open, explore and autoplay
with random-junk comment lines in between: the Sality autorun pattern.

- SD Scan store app (deploy/apps/sdscan/1.0, requires "sd", not seeded):
  walks the card a small page per tick, lists what it finds and why, and
  removes it after a confirmation screen with Cancel first. It says on
  every screen that it only removes files it recognises and that
  formatting the card is the safe fix. On older firmware it still finds
  threats by name but cannot remove them.
- Firmware: wada.sd.check(path) and wada.sd.remove(path), plus paging for
  wada.sd.list(path, start, max) and caps().sd_clean. What counts as a
  threat lives in SdThreat.h: autorun.inf, Windows program, script and
  shortcut extensions, or a real MZ+PE header under any name. remove()
  classifies again in firmware and refuses anything else, so no app can
  use it to delete tiles, backups or chat history. It clears read-only,
  hidden and system first, because FAT refuses to delete a read-only file.
- A warning when a card with Windows malware in its top folder is mounted,
  at boot or on insert, offering SD Scan (or the Store).
- Tests: test/test_sd_threat.cpp, and SD Scan harness scenarios including
  the real infected card's root. Removal checked on a T-Deck.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 13:33:30 +02:00
Kaj Schittecat 65ea09ea89 Merge PR #316: WAV/MP3 playback for Lua apps
oumike. Closes #315 (pisti87's request).

Two conflicts, both resolved by keeping BOTH sides rather than choosing:

  - sdRuntimeLifecycleBusy() gained an audio-playback source here and a web
    reader source in #317. They are independent consumers of the same card and
    both have to gate the mount lifecycle. The reader's self-exclusion is kept:
    it calls this from its own task while holding the card and would otherwise
    deadlock against itself.
  - The Lua harness caps table needed sd_list from #312 as well as the audio
    flags, and the test order needed the wardrive suite from #324 as well as
    audio_api.

Built on all eight S3 envs and both ESP32-P4 targets.
2026-08-27 10:49:59 +02:00
Michael A. Cojocari 417a52caa1 Fix Wardrive UTF-8 text handling
Signed-off-by: Michael A. Cojocari <michael.cojocari@gmail.com>
2026-08-25 16:12:24 -04:00
Michael A. Cojocari acdcc6e7fc Work 2026-08-23 20:32:26 -04:00
Christopher Van Hoose 6cc0c1e56e Make the WMM block generated, and the tests reviewable
The declination model landed as 4.7 KB of constants pasted into a Lua app,
generated by a script that lived in out/ -- which is gitignored, holds firmware
bins, and is where the app's own "Regenerate:" comment pointed. So the pointer
dangled for anyone who cloned the repo, and nobody but me could answer the
first fair question a reviewer would ask about that block of magic numbers:
where did it come from, and how do I know it is right.

  scripts/wmm/       WMM.COF + NOAA's 100 official test values (both upstream
                     and unmodified), the float64 reference, the generator,
                     verify.py, and a README covering provenance, regeneration
                     and how to move to WMM2030.
  scripts/lua-harness/  the host harness, with run.sh so it is one command.

Neither goes in test/: that is PlatformIO's directory and a harness with a
main.c would be swept into `pio test`. scripts/ already holds this repo's dev
tooling, test_companion_serial.py included.

The block in the app is now genuinely generated rather than hand-pasted:

    scripts/wmm/gen_lua.py --update <app>    rewrite it
    scripts/wmm/gen_lua.py --check  <app>    fail, with a diff, if it drifted

--check catches coefficients updated without regenerating, or a block edited by
hand. The generator owns the `local declination / do ... end` wrapper too, and
that is the point: the tables are named G/H/GD/HD, gpscompass uses a global H
for the screen height, and an unscoped `local H` silently ate it. Hand-wrapping
is how that happened, so hand-wrapping is now not a step.

Verification, all reproducible from a clean clone:
  scripts/wmm/verify.py            100 NOAA values, worst D error 0.005 deg
  scripts/lua-harness/run.sh       10 scenarios, incl. the generated Lua in
                                   the device's own LUA_32BITS interpreter --
                                   0.0002 deg vs NOAA, worst tick 12k of 100k

Also refreshes the LUA_APPS.md paragraph, which still advertised the O and F
keys that were removed and quoted harness numbers from before tilt
compensation.
2026-08-22 13:30:36 -04:00