跳轉到

reyee — Ruijie / Reyee router inspector

Read-only CLI for poking at a Ruijie / Reyee "Shinya" router's eWeb API without opening a browser, with one guarded writer (set-channel). Sister tool to mi-router (Xiaomi) and sms (Huawei HiLink).

Tested against a Reyee home router on the LuCI-based eWeb firmware (theme eweb-ehr, ReyeeOS 1.275.2406 / EW_3.0, CN region). The H30M / H30S family and similar EW_3.0 builds should work; unknown endpoints fail soft (a JSON error payload, not a crash).

How the API works

Auth is eWeb's GibberishAES handshake — which is OpenSSL AES-256-CBC in the Salted__ format (-md md5). The login password is AES-encrypted under a static passphrase k, itself AES-decrypted from a blob baked into the login page under the passphrase eweb. The ciphertext is POSTed to /cgi-bin/luci/api/auth; the response returns {sid, token, sn} and sets a cookie <sn>=<sid>.

Everything after login is a JSON-RPC POST to /cgi-bin/luci/api/cmd?auth=<sid> with a body of {"method":"<bin>.<verb>","params":{"module":"<m>", ...}}. The web UI derives <bin> from a CLI tool name: dev_config → devConfig, ac_config → acConfig, dev_sta → devSta. So "read the radios" is dev_config get -m radio, i.e. {"method":"devConfig.get","params":{"module":"radio"}}.

In a self-organizing (mesh) network, add "remoteIp":"<sub-ap-ip>" to a params object to target a sub-router instead of the one you logged into.

Install

Deployed automatically by chezmoi:

  • ~/.dotfiles/bin/reyee — the CLI (uv-script; first run resolves deps)
  • ~/.config/reyee/config.toml.example — starter config

uv must be installed (it is, via the bootstrap script).

First-time setup

cp ~/.config/reyee/config.toml.example ~/.config/reyee/config.toml
chmod 600 ~/.config/reyee/config.toml
$EDITOR ~/.config/reyee/config.toml   # set host + password

# Or let reyee prompt on first call and save for you
reyee login-test

Resolution order (first non-empty wins):

  1. --host / --password CLI flags
  2. ~/.config/reyee/config.toml
  3. REYEE_HOST / REYEE_PASS env vars
  4. interactive getpass prompt (TTY only; offers to save)

Default: host = 192.168.110.1. The router is password-only (no username).

Commands

reyee info                                  # identity + per-radio + mesh summary
reyee radios                                # per-radio channel / TX power
reyee wifi                                   # configured SSIDs
reyee wifi --show-passwords                  #   …with cleartext PSKs
reyee clients                                # connected clients (band/ch/RSSI)
reyee mesh                                   # mesh APs + sub-router backhaul diagnostics
reyee login-test                             # verify creds, no other output
reyee raw devConfig.get --params '{"module":"radio"}'   # any JSON-RPC method
reyee raw devSta.get --params '{"module":"topology"}'   # mesh topology

# Guarded writer — dry-runs unless --yes:
reyee set-channel --band 5g --channel 36                 # dry-run
reyee set-channel --band 5g --channel 36 --yes           # apply
reyee set-channel --band 5g --channel auto --ap 192.168.110.91 --yes  # sub-AP
reyee set-channel --band 5g --channel auto --yes         # restore auto

Global flags: --host, --password, --timeout (before or after the subcommand).

set-channel — the one writer, and why it's guarded

Every other subcommand is read-only. set-channel is the single exception because changing a channel is the most common reason to reach for the CLI, but it is also disruptive: applying a channel change briefly drops every Wi-Fi client on that radio. So it:

  • dry-runs by default — prints the current→target and the exact payload it would send, and changes nothing. Add --yes to actually apply.
  • minimal-diff — reads the live radioList, changes only the one channel field, and writes the rest back verbatim (so it never clobbers bandwidth / TX power / country).
  • warns on DFS — 5 GHz channels 52–64 / 100–144 require radar detection and are poor for latency-sensitive use; the tool flags them.
  • targets sub-APs — --ap <ip> routes the change to a mesh node via the eWeb remoteIp mechanism (each AP holds its own channel; "以下配置只对当前设备生效"). Note: dual-band sub-routers using 5 GHz wireless backhaul (wirelessBridge) share their single 5 GHz radio between the mesh uplink (@Ruijie-Repeater) and local AP service — keep a wireless sub-router's 5 GHz channel on auto (or the exact same channel as the main router), and warns if you try to pin a fixed 5 GHz channel on a wirelessBridge sub-AP.

Other writes (SSID, password, reboot, reset, band steering, …) are intentionally not wired up — do those in the web UI.

Diagnosing offline sub-routers (reyee mesh)

When the web UI's 子路由管理 → 子路由列表 greys out a sub-router (status: OFF), run reyee mesh. It cross-checks four router tables (ap_list, topology, user_list, and dhcp_lease) to separate two distinct failure modes:

  1. Listed on the backhaul, but L3 status decides the verdict — the sub-router MAC shows up on the hidden @Ruijie-Repeater 5 GHz backhaul interface (rax5) in user_list, yet ap_list reports OFF and remoteIp RPCs fail with get sid fail. reyee mesh then pings the sub-router from the local host and checks ARP:
  2. ping OK → only the mesh control plane is down. Reboot it from its own web UI; if it stays OFF, delete it under 子路由管理 and re-pair (management password / networkId out of sync).
  3. no ping, ARP unresolved → the sub-router's OS is down. The user_list entry is not proof of life: it lingers (frozen onlinetime / activeTime, counters not reset) after the node drops. Power-cycle once (unplug 30 s). If it does not return, its LED cycles red → off (boot loop), the UI flips green → red, or it buzzes / whines, the built-in power supply has failed — unplug it and RMA; a factory reset will not help. (Real case, 2026-10: H30S with exactly this signature.)
  4. L2 backhaul not associated — the sub-router is absent from user_list. Check power/LEDs, distance, or whether its 5 GHz channel was pinned to a different channel than the main router.

Useful raw modules

Read with reyee raw devConfig.get --params '{"module":"<m>"}' (or acConfig / devSta):

bin module what
devConfig radio per-radio channel / txpower / hwmode (the RF view)
acConfig wireless SSID list (⚠ includes cleartext PSK)
devSta user_list connected clients: band, channel, RSSI, rate (includes @Ruijie-Repeater backhaul)
devSta ap_list all mesh APs: SN, model, IP, MAC, status (ON/OFF), relayMode, channel
devSta topology mesh topology tree (which clients/sub-APs hang off which AP)
devSta dhcp_lease active DHCP leases (List of MAC, IP, hostname, remaining minutes)
devSta dev_diag router self-diagnostic checklist (ports, WAN, DHCP, loop, DNS, cloud)
devSta neighbor neighbor APs the router has scanned
acConfig roam 802.11k/v roaming / band-steering policy

Read-only-by-design caveat

A side effect of the auth handshake: each fresh login creates a new session. Running reyee repeatedly while you have the web UI open in a browser tab can invalidate the browser's session and bounce it to the login screen.

See also

  • mi-router — sister tool for Xiaomi / MiWiFi routers.
  • sms — sister tool for Huawei HiLink routers.
  • WiFi latency spikes — the playbook this tool was built for (diagnosing periodic latency on Wi-Fi).
  • ping-monitor — record ping + flag spikes.