JapyScope Remote — Codes Reference

Living document — extend freely as new codes/diagnostics are introduced. Covers every “enter a code” flow on the device plus a maintainer-facing error/status code list for support and troubleshooting.

Key map (physical 3×4 keypad + KY-040 encoder + optional external joystick)

Key Meaning
1, 3 unused (reserved)
2 Jog Up — only handled on ALIGN_JOG/TRACK (see firmware/ui/controller.py’s JOG_DIRECTIONS/_jog()); a plain digit everywhere else
4 Jog Left — same screens
6 Jog Right — same screens
8 Jog Down — same screens
7 Jog speed (opens speed adjust)
9 Back, one level — everywhere except inside SmartSearch text entry (see below)
0 digit “0” / T9 letter entry
FN2 reserved outside SmartSearch (home-screen easter egg); inside SmartSearch: cancel/back out
BKSP delete last character (text entry)
Encoder rotate scroll (menus, lists)
Encoder push open / select / confirm

External joystick (optional — docs/WIRING.md)

A KY-023-style dual-axis joystick module, if connected (shared.db’s joystick_enabled), is a second, independent source of the very same events above — see firmware/hal/input/joystick.py and keys.py:

Joystick input Reuses / adds
Y-axis up/down ENC_UP/ENC_DOWN — same as rotating the encoder (scroll menus/lists)
Push button ENC_PUSH — same as pressing the encoder (select/confirm)
X-axis left/right JOY_LEFT/JOY_RIGHT (new, joystick-only) — Jog Left/Right on ALIGN_JOG/TRACK, same as keypad 4/6

It’s a drop-in alternative to the encoder for navigation, and the more natural way to do the 4-directional jog above (keypad 2/4/6/8 double as digit-entry T9 keys everywhere else; the joystick’s axes never do).

No diagonal jog: a push deflected on both axes at once (e.g. up-and-right) is deliberately treated as no input rather than snapped to one axis — the mount only jogs one cardinal direction (N/S/E/W) at a time. Push the stick cleanly toward one direction (see firmware/hal/input/joystick.py’s _direction_from_axes()).

SmartSearch T9 exception

Everywhere else in the system, “9” always means “back, never an action” — this is a deliberate, load-bearing rule (state machine code in firmware/ui/ relies on it). Inside the SmartSearch/TYPE screen only, this is flipped: “9” is a normal T9 letter key (classic-phone layout — w/x/y/z/9), and FN2 becomes “cancel out of SmartSearch” instead. This was a deliberate v0 decision (16.9) to match old-phone muscle memory, overriding the original mockup’s design. Do not “fix” this back without re-confirming with Ján — it looks inconsistent but is intentional.

Full T9 map (firmware/hal/input/keys.py order, letters per digit):

Key Letters
1 a b c
2 d e f
3 g h i
4 j k l
5 m n o
6 p q r s
7 t u v
8 w x y z
9 w x y z — SmartSearch-only exception (everywhere else, 9 = back); yes, this duplicates key 8’s letters, so w/x/y/z can be typed via either key — that’s intentional, the point is giving 9 the “old phone” wxyz muscle memory without disturbing keys 1–8
0 0 (digit only, no letters)

Dev Tools codes

4-digit code entered via rotate-per-digit (Home → Dev Tools). The real implementation lives in firmware/ui/controller.py (ControllerUI._devtools_code()) — that’s what runs on the device. docs/mockup.html’s handleDevtools() is a browser-only prototype of the same behavior, kept in sync for prototyping/demo purposes, but it is not a substitute for the firmware implementation and a code must not be marked functional in this table until it actually works in firmware/ui/. Add new Dev Tools codes here as they’re introduced, and wire them into both firmware/ui/controller.py and the mockup — don’t scatter magic numbers in firmware/ui/ without a matching entry in this table.

Code Action Notes
0000 Exit Dev Tools  
0022 Set repo functional — opens repo selection (currently one option: jan-tdy/japyscope-remote); persists to settings.update_repo, read by install/update.py
0033 Set update channel functional — opens channel selection, Stable only / Stable + prereleases; persists to settings.update_channel, read by install/update.py (prerelease channel lists /releases and skips drafts instead of hitting /releases/latest)
0044 SSH functional — runs systemctl enable --now ssh, then shows the result plus IP/hostname and login user
1001 Enable more INDI drivers (camera, focuser, etc.) coming soon — placeholder message only, no drivers selectable yet
1111 Restart INDI server functional — calls IndiServerManager.restart() (via ControllerUI.indi_manager, wired from firmware/main.py); shows “unavailable” in --simulate mode, which has no managed indiserver
1234 Show system info functional — IP/hostname, uptime (/proc/uptime), INDI connection status (no fake version string — nothing exposes a real INDI version yet)
5000 Launch Wi-Fi setup wizard functional — same flow as a factory-reset Wi-Fi join
5555 Select mount driver functional — opens driver selection (currently one option: Sky-Watcher Alt-Az GTi)
9600 Select communication interface functional — opens interface selection (currently one option: RJ12 direct to mount)
9955 Run custom script functional — lists .sh scripts found in {homepath}/custom (JAPYSCOPE_CUSTOM_SCRIPTS_DIR env var overrides the directory) and runs the selected one via /bin/sh, fire-and-forget
9999 Easter egg (astronomer/Moon joke) functional, cosmetic only
4200 Easter egg (“42.”) functional, cosmetic only
1957 Easter egg (Sputnik) functional, cosmetic only
0905 Easter egg (“Protocol 09: the code stays free — JapySoft”) functional, cosmetic only
anything else Shows “Invalid code.”  

None of the above currently require the sudo password — add the requirement here (and enforce it in firmware/ui/) the day a Dev Tools code becomes genuinely destructive (e.g. a future factory-reset code). 9955 runs arbitrary local .sh files, but only ones the device owner already placed in {homepath}/custom themselves — same trust level as having a shell on the device, not a remote-execution surface.

Sudo password

Web UI access code

Maintainer-facing status / error codes (new, beyond the mockup)

These don’t exist in the HTML mockup — added here for docs/TROUBLESHOOTING.md and systemd journal greps once the firmware app has real logging. Extend as new failure modes are found during bring-up.

Code Meaning Where logged
INDI-001 indiserver failed to start (binary not found / bad driver name) japyscope-app journal
INDI-002 indiserver started but mount driver never reported CONNECTED japyscope-app journal
INDI-003 indiserver crashed and was restarted by the watchdog japyscope-app journal
INDI-004 Manual jog (keypad 2/4/6/8 or joystick, ALIGN_JOG/TRACK) requested but IndiClient.jog() isn’t wired up against the live driver yet japyscope-app journal
OTA-001 update check failed (no internet / GitHub API unreachable) japyscope-app journal, Web UI → System
OTA-002 downloaded release failed checksum verification same
OTA-003 new release failed its post-install health check, rolled back same
WIFI-001 AP-mode setup hotspot failed to start hostapd/dnsmasq journal
SEARCH-001 SmartSearch online lookup timed out / no internet — fell back to local catalogs only japyscope-app journal
SYS-001 Daily apt-get update/upgrade step failed (best-effort — never blocks the JapyScope release step) japyscope-update journal