Skip to content

CLI Reference

Every beebo command with its arguments and options, generated from the command line itself. For what a command is for and how to read its output, see Commands, Monitoring, Health Check and Analysis. beebo COMMAND --help prints the same text.

The global options, valid before any command, pick the node and the transport.

beebo

CLI for Heltec ESP32-S3 V4.3 MeshCore Beebo Nodes.

Options

  • --ble, -b ADDRESS: Connect via BLE to ADDRESS ('scan' to discover). Default: scan.
  • --tcp, -t HOST: Connect via TCP to HOST[:port]. Default: beebo.
  • --usb, -u PORT: Connect via USB serial PORT. Default: /dev/ttyACM0.
  • --raw, -r: Force the plain-text CommonCLI protocol instead of the companion binary protocol -- named for the protocol, not the node's live role (either role answers over either protocol on multi_role).
  • --log-level, -l LEVEL: Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL. Default: WARNING.
  • --profile, --no-profile: Enable CLI level command profiling.
  • --interactive, -i: Open a 'beebo() -> ' prompt; connect/release manage the device session explicitly, and any other command connects implicitly on first use. Ctrl-D or 'exit'/'quit' to quit.
  • --version, -v: Show version and exit.
  • --install-completion: Install completion for the current shell.
  • --show-completion: Show completion for the current shell, to copy it or customize the installation.

Commands: admin, advert, analyze, battery, channels, check, clock, config, connect, contacts, discover, measure, messages, monitor, neighbors, ota, profile, reboot, region, release, run, rxlog, settings, status, trace, traffic

beebo admin

Remote node administration authorization and commands.

beebo admin <target> login [password] # mesh admin login (prompts if omitted) beebo admin <target> logout beebo admin <target> forget # drop the cached password beebo admin <target> cmd <command...> # run an admin CLI command remotely beebo admin <target> cli <command...> # run a companion CLI command remotely (needs cli-allow on the target's side) beebo admin <target> cli-allow [on|off] # show or set whether <target> may run CLI commands on this node beebo admin <target> status beebo admin <target> neighbors beebo admin <target> telemetry beebo admin <target> owner # anonymous, no login needed beebo admin <target> regions # anonymous, no login needed beebo admin <target> clock # anonymous, no login needed beebo admin <target> acl beebo admin <target> mma <start> <end> # sensor (type 4) nodes only beebo admin <target> transport [<usb|ble|tcp> [on|off]] # get all, get one, or set one beebo admin self cmd <command...> # run on THIS node directly, no contact/login/mesh relay needed

<target> is a contact name or public-key hex prefix, as shown by beebo contacts. self is special: see _do_self_cmd.

Arguments

  • TARGET: Contact name or public-key hex prefix.
  • ACTION: Admin operation to run against target.
  • ARGS (optional): Action-specific arguments (e.g. a password for login, the command text for cmd, or <start> <end> for mma).

Options

  • --use-cached: login only: reuse the cached password instead of prompting, if one is on file.

beebo advert

Send a direct or flood advert.

Commands: direct, flood

beebo advert direct

Send a direct (zero-hop) advert: only nodes in radio range hear it.

beebo advert flood

Send a flood advert: the whole mesh hears it.

beebo analyze

Offline analysis of downloaded data.

Options

  • --dir, -d DIR: Root directory to read from -- same root monitor decode --save writes to, laid out as <dir>/<node>/data/<profile>/. Default ~/.beebo/nodes.
  • --style STYLE: Chart style: braille, fhd, hd, sd, dot, sample, star, bar (line markers finest first; sample (+) and star (*) draw unjoined points; or vertical bars). Default: braille.

Commands: latency, link-quality, list, mesh, neighbors, noise, packets, paths, rate, retries, routing, tuning

beebo analyze latency

Size the windows that declare a packet duplicated and delivered.

Checks the time limits the node judges packets with against the captured traffic. A packet is duplicated when we already heard it; one we route is echoed when a neighbor relays it and we hear that relay within the echo window of our transmission. The report shows the limits in use, what falls inside and outside them, the delay distributions, and the window each needs. --seen-window and --max-latency try other limits.

Of the valid rx packets received, some are duplicated (a packet we already heard) and some are routed to neighbors; a routed packet is delivered when its echo, a duplicated packet a neighbor causes by relaying it, comes back. Each declaration waits a window for it: seen_window for duplicated (from the first reception), the echo window for delivered (from our transmission), and each window holds its candidates in a queue: the dedup table (160 first receptions) and the echo ring (16 routed packets). The window must cover the late arrivals and the queue must not fill before it ends, or duplicated and delivered packets are missed. The report gives the delay distributions (percentiles and a histogram each), the window each needs, and whether its queue holds that long.

The echoes come from the echo events of a raw monring .bin; any other set gives the duplicated packets only. A duplicate is an echo when the RX record flags that the hop before the last in the path is our own hash (monitor format 4, exact), else by the hop count (the first reception's + 2, an upper bound: a route of the same length through other repeaters reads the same). --detail adds the full echo window table, the hop tables, the duplicate report and its slowest pairs. analyze timing and analyze dup-latency still print one half each.

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --limit, -l N: Show at most N packets. 0 = no limit. Default: 20.
  • --max-latency SECONDS: Discard matched pairs whose Δt exceeds SECONDS as a pkt_hash collision (the same hash reused by an unrelated later packet), not a real duplicate -- a genuine dedup-table hit arrives within the dedup window, tens of seconds at most. 0 disables this filter. Default: 100.0.
  • --seen-window SECONDS: What-if: judge the echoes against this seen_window instead of the one the node ran with (a settings event in the capture, else the firmware default).
  • --json: Print the statistics as JSON.
  • --detail: Also print the echo window table, the hop tables, the duplicate report and the slowest duplicate pairs.

Per-neighbor SNR/RSSI summary from a downloaded monitor set, with a mean +- std chart per metric. With --hash/--name: statistics and distribution charts for one neighbor (--trend adds the hourly charts, --table the text tables).

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --resolve, -R, --no-resolve: Resolve each neighbor hash to a contact name using the contacts.json cache. On by default.
  • --min-packets, -m N: Only include neighbors with at least N RX packets. Default: 1.
  • --by-name, -N, --by-hash: Aggregate by resolved contact name instead of raw neighbor hash, merging different hash-prefix lengths seen for the same node. Needs --resolve (on by default); hashes that don't resolve still group by hash. Off by default.
  • --sort-by, -s n|snr-mean|snr-std|snr-min|snr-max|rssi-mean|rssi-std|rssi-min|rssi-max: Sort the summary table by this column, descending. n = packet count (default).
  • --chart, -c, --no-chart: Also write an SVG bar chart (SNR/RSSI mean ± std per neighbor) alongside the source file, one per radio-config permutation. Off by default.
  • --hash HEX: Show detailed stats (SNR/RSSI distribution + trend) for one neighbor matched by hash or hash-prefix, instead of the summary table. Mutually exclusive with --name.
  • --name NAME: Show detailed stats for one neighbor whose resolved contact name starts with NAME (case-insensitive), instead of the summary table. Mutually exclusive with --hash.
  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --snr, --no-snr: Include SNR in the summary table / detail view. On by default.
  • --rssi, --no-rssi: Include RSSI in the summary table / detail view. Off by default.
  • --trend, --no-trend: Include the hourly SNR/RSSI trend table. Only applies to the single-neighbor detail view (--hash/--name). Off by default.
  • --bin-size DB: Histogram bin width in dB/dBm, for the single-neighbor detail view (--hash/--name). Default: 1.
  • --align-bins, --no-align-bins: Shift the histogram bin grid so the first bin is centered on the nearest integer dB/dBm value, instead of on the raw data minimum. Only applies to the single-neighbor detail view (--hash/--name). On by default.
  • --lna on|off: Only include samples captured with the FEM LNA on/off. Records with no known radio config are dropped when set. Unfiltered (both) by default.
  • --rxgain on|off: Only include samples captured with SX1262 RX boost (rxgain) on/off. Records with no known radio config are dropped when set. Unfiltered (both) by default.
  • --min-snr DB: Only include samples with SNR >= DB. Records with no known SNR are dropped when set. Unfiltered by default.
  • --max-snr DB: Only include samples with SNR <= DB. Records with no known SNR are dropped when set. Unfiltered by default.
  • --table, --no-table: Also print the per-row table (one row per window or hour; long on multi-day sets). Off by default.

beebo analyze list

List downloaded monitor sets under <dir>/<node>/data/<profile>/ with basic stats, plus the binary sets themselves (monring_<date>.bin day files and any monitor pull --to NAME named <NAME>.bin) under <dir>/<node>/data/. --profile only filters the combined sets (binary sets aren't split per profile). Scoped to the invoking node alias when run through a real node symlink (e.g. gatto-mr analyze list); a bare beebo analyze list lists every node's data.

Options

  • --device NAME: Only sets under a device directory whose name starts with NAME (case-insensitive). Unfiltered by default.
  • --profile NAME: Only sets under a radio-profile directory whose name starts with NAME (case-insensitive). Unfiltered by default.
  • --start WHEN: Only sets modified at/after WHEN -- 'HH:MM' (today) or 'YYYY-MM-DD HH:MM'. Unfiltered by default.
  • --end WHEN: Only sets modified at/before WHEN -- 'HH:MM' (today) or 'YYYY-MM-DD HH:MM'. Unfiltered by default.
  • --min-size KB: Only sets at least KB kilobytes. Unfiltered by default.
  • --max-size KB: Only sets at most KB kilobytes. Unfiltered by default.

beebo analyze mesh

A console map of the nodes we hear and how their packets connect them.

Needs a raw monring .bin and the neighbors.json cache (beebo neighbors pull) for positions: ours and those the neighbors advertise. A schematic that fills the console, not to scale: the nodes keep their west-to-east and north-to-south order and are spread by rank. Each direct neighbor is a link and, on a capture with rx path mode on, the busiest links between the other repeaters of the paths are added; a link is colored by its packets, blue for the least to red for the most. A table lists the numbered nodes with their counts, distance and compass direction.

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --max-latency SECONDS: Discard matched pairs whose Δt exceeds SECONDS as a pkt_hash collision (the same hash reused by an unrelated later packet), not a real duplicate -- a genuine dedup-table hit arrives within the dedup window, tens of seconds at most. 0 disables this filter. Default: 100.0.
  • --seen-window SECONDS: What-if: judge the echoes against this seen_window instead of the one the node ran with (a settings event in the capture, else the firmware default).
  • --resolve, -R, --no-resolve: Resolve each neighbor hash to a contact name using the contacts.json cache. On by default.
  • --top N: Neighbors drawn, busiest first. Default: 8.
  • --links N: Links between repeaters drawn, busiest first; 0 draws none. Default: 6.
  • --width COLS: Map width in columns (default: the console width).
  • --height ROWS: Map height in rows (default: the console height less the text).
  • --json: Print the nodes and links as JSON.

beebo analyze neighbors

Neighbor activity: node/packet counts and a packets-per-neighbor chart; --hourly adds a packets-by-hour-of-day chart (--table also puts the hourly counts in the table).

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --resolve, -R, --no-resolve: Resolve each neighbor hash to a contact name using the contacts.json cache. On by default.
  • --min-packets, -m N: Only include neighbors with at least N RX packets. Default: 10.
  • --hourly, -H, --no-hourly: Break down each neighbor's packet count by local hour-of-day. Off by default.
  • --by-name, -N, --by-hash: Aggregate by resolved contact name instead of raw neighbor hash, merging different hash-prefix lengths seen for the same node. Needs --resolve (on by default); hashes that don't resolve still group by hash. Off by default.
  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --table, --no-table: Also print the per-row table (one row per window or hour; long on multi-day sets). Off by default.

beebo analyze noise

Noise floor statistics, distribution and hourly trend from a downloaded monitor set (--table adds the histogram and hourly tables).

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --table, --no-table: Also print the per-row table (one row per window or hour; long on multi-day sets). Off by default.

beebo analyze packets

Search/filter individual rx/tx packets from a downloaded monitor set.

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --resolve, -R, --no-resolve: Resolve each neighbor hash to a contact name using the contacts.json cache. On by default.
  • --resolve-by RULES: The criteria used to resolve a colliding hash prefix to one hash/name: a comma list of rules, tried in order, drawn from active, heard, distance. A rule that doesn't apply to a candidate (e.g. no known location, for distance) is skipped rather than counted as a tie. Default: active,heard,distance.
  • --name NAME: Only packets whose resolved node name starts with NAME (case-insensitive). Needs --resolve (on by default). Mutually exclusive with --hash.
  • --hash HEX: Only packets whose neighbor/destination hash matches or is a prefix/extension of HEX. Mutually exclusive with --name.
  • --kind rx|tx: Only rx or tx packets. Omit for both.
  • --type TYPE: Only packets whose payload type starts with TYPE (case-insensitive), e.g. advert, text, ack, path, trace. Pass with no value to list the available types.
  • --route ROUTE: Only packets whose route type starts with ROUTE (case-insensitive), e.g. direct, flood. Pass with no value to list the available route types.
  • --outcome TEXT: Only packets whose outcome contains TEXT (case-insensitive) -- an rx verdict/routing label (accepted, forwarded, dup, wrong_hop, ...), or for tx: the send result (ok, timeout) plus, once resolved, 'ack:<verdict>' (DIRECT) or 'echo:<verdict>' (FLOOD), verdict one of success/timeout/overflow.
  • --invalid-only: Only rx packets whose header/path did not parse. Off by default.
  • --where EXPR: Filter expression over a record's raw fields -- same COLUMN<op>VALUE mini-language as monitor decode's --where (==, !=, >=, <=, >, < or a bare = for ==; ~ is a case-insensitive substring match; and/or, parens): --where "(snr>=5 and kind=rx) or (kind=tx and status=ok)" --where node~f5 . Fields: ack, ack_nbr, ack_nbr_hash, ack_nbr_name, airtime_ms, hops, invalid, kind, node, node_hash, node_name, pkt_hash, pos, route, routing, rssi, snr, status, type. Repeatable (ANDed). Default: [].
  • --hide COLS: Comma-separated column names to omit from the table (data is still fetched/filtered/sorted as usual). Choose from: pos, time, kind, node, type, route, hops, status, routing, ack, ack_nbr, snr, rssi, airtime, hash. Example: --hide airtime,status
  • --short-names, --full-names: Truncate a resolved name (Node/AckNbr) to its first 5 characters so the table stays narrow. Raw hashes are unaffected -- they're already short. On by default.
  • --group-by COLS: Comma-separated field(s) to group the matched packets by, printing one row per distinct combination with its packet count instead of the usual per-packet table. Groups are sorted by count (descending, or ascending with --asc) and --limit caps the number of groups shown, not packets -- --sort-by/--hide don't apply. Fields: ack, ack_nbr, ack_nbr_hash, ack_nbr_name, airtime_ms, hops, invalid, kind, node, node_hash, node_name, pkt_hash, pos, route, routing, rssi, snr, status, type. Example: --group-by kind,type
  • --hops N: Only rx packets received with exactly N path hashes on the wire -- 0 means heard directly, before any relay appended its own hash (same check the direct-neighbor table uses for an ADVERT). tx packets never match, since they have no hop count of their own. Records from before MonRing ABI version 2 have no known hop count and never match any N. Unfiltered by default.
  • --sort-by, -s time|kind|node|type|route|hops|status|routing|ack|ack_nbr|snr|rssi|airtime|hash: Sort by this column: time (capture order, default), kind, node (name if resolved else hash), type, route, hops, status, routing, ack, ack_nbr (the confirming neighbor on a successful tx echo), snr, rssi, airtime, hash. A record missing the sorted field (e.g. hops on a tx packet) sorts as the lowest value.
  • --asc, --desc: Sort ascending instead of descending. Off (descending) by default, so --sort-by time shows the most recent packets first.
  • --limit, -l N: Show at most N packets. 0 = no limit. Default: 100.
  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.

beebo analyze paths

The routes to repeaters and the links between them.

Needs a raw monring .bin captured with rx path mode on (monitor capture path on). For each repeater seen in a path, lists the chains of hops from us to it with the share of its packets that took each, and ranks the links between repeaters.

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --resolve, -R, --no-resolve: Resolve each neighbor hash to a contact name using the contacts.json cache. On by default.
  • --top N: Repeaters and links shown, busiest first; 0 shows all. Default: 10.
  • --json: Print the routes and links as JSON.

beebo analyze rate

Per-window RX/TX packets-per-minute: summary statistics and a time-series chart from a downloaded monitor set.

Every value here is derived from MonRing's existing MON_RX/MON_TX record timestamps, no firmware change needed. Idle stretches of an hour or more (device off / no capture) show as a gap in the chart, not as zero traffic.

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --window, -w MINUTES: Window size in minutes (not seconds) for aggregation. Default 5.
  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --json: Print one JSON object per window instead of a table -- for piping into further analysis (e.g. correlating candidate parameter values against these indicators offline). Off by default.
  • --table, --no-table: Also print the per-row table (one row per window or hour; long on multi-day sets). Off by default.

beebo analyze retries

Route retry outcome from a downloaded monitor set: how many forwards were retried after their echo timed out, how many retries were heard (echoed), went unanswered or are still pending, and the extra airtime they cost.

A retry is a FLOOD tx with attempt > 0 (the same packet hash as its first send, at the coding rate of repeater.routing.retry); its verdict is the echo event that names that attempt. Rescue rate is echoed of the retries that have a verdict.

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --json: Print the statistics as JSON.

beebo analyze routing

Why a packet we route is, or is not, echoed by a neighbor.

Needs a raw monring .bin (its echo verdicts and RX path flags). Each packet we sent on lands in one scenario: a 0-hop echo (a neighbor relayed ours), an n-hop echo (relayed on by more repeaters), another route (a neighbor had it already), an echo after the window, or silence, told apart by whether a duplicate was heard before our send (a neighbor already relayed it). The report draws one typical packet of each as a sequence of events, tests whether neighbors stay silent once another has relayed the packet, and ranks the last-hop neighbors and the hops of the first receptions. The RX record names the last hop only: the topology is the direct neighbors, further repeaters appear as hop counts.

Arguments

  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --max-latency SECONDS: Discard matched pairs whose Δt exceeds SECONDS as a pkt_hash collision (the same hash reused by an unrelated later packet), not a real duplicate -- a genuine dedup-table hit arrives within the dedup window, tens of seconds at most. 0 disables this filter. Default: 100.0.
  • --seen-window SECONDS: What-if: judge the echoes against this seen_window instead of the one the node ran with (a settings event in the capture, else the firmware default).
  • --resolve, -R, --no-resolve: Resolve each neighbor hash to a contact name using the contacts.json cache. On by default.
  • --top N: Neighbors shown, busiest first; 0 shows every neighbor. Default: 10.
  • --json: Print the statistics as JSON.

beebo analyze tuning

Dynamic-tuning optimizer analysis: per-window indicators, the setting trial, and per-setting effect.

Commands: effect, indicator, indicators, phi, trial

beebo analyze tuning effect

How setting assignments affect the delivery indicators.

The capture is split into intervals of constant assignment. Each assignment gets its pooled indicators (eval confirm ratio and goodput, rx/tx rates, delivery, wrong-hop, SNR, noise), then a delta against the longest-running assignment computed hour by hour over the hours both ran, so time-of-day load differences cancel. z is the delta over its standard error across those hours.

Arguments

  • [FILE] (optional): One raw monring_<date>.bin day file. A relative FILE is resolved against --dir. Defaults to the most recently pulled one.

Options

  • --from NAME: Read the file monitor pull --to NAME writes instead of the day files.
  • --param, -p KEY: Setting key(s) that define an assignment (repeatable), e.g. lna, rxgain, tx_power, a setting name. Default: every key that changes value in the capture.
  • --min-hours MIN_HOURS: Hide assignments that ran less than this. Default: 0.25.
  • --json: Print JSON instead of tables.
beebo analyze tuning indicator

Look into one indicator's peculiarities: dips (or bursts, for an indicator where high is bad) with their depth and spacing, the share of time in a bad state, level shifts, a repeat period, the time-of-day profile, and what moves with the dips: other indicators (co-movement and lead/lag), the load at which it degrades, and setting changes, trial starts and fault events near them. It ranks suspects, it does not prove a cause.

Arguments

  • INDICATOR: An analyze tuning indicators row, e.g. Score, 'Received', 'Noise dBm' (case and spaces ignored).
  • FILE (optional): A combined monitor_<tag>.csv/json set written by monitor decode --save, or a raw monring_<date>.bin file straight from monitor pull (decoded on the fly, no --save needed first). A relative FILE is resolved against --dir, not the current directory; a bare name with no directory (whip, whip.bin) that is not a file there is looked up as a dataset NAME. Defaults to the most recently pulled raw set, falling back to the most recently saved combined set if no raw data has been pulled, under <dir>/data/ (--dir, default ~/.beebo). A dataset can also be named directly by writing its NAME right after analyze, before the report, e.g. analyze trials rate instead of analyze rate trials.bin -- see analyze list's File column for NAMEs available. NAME and FILE are mutually exclusive.

Options

  • --window, -w MINUTES: Window size in minutes (not seconds) for aggregation. Default 5.
  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --sensitivity, -k K: A dip is a run of windows K robust deviations (MAD) below the median; lower finds more. Default: 3.0.
  • --limit LIMIT: Longest dips or bursts listed (deepest first). Default: 10.
  • --json: Print one JSON object per window instead of a table -- for piping into further analysis (e.g. correlating candidate parameter values against these indicators offline). Off by default.
  • --score NAME=WEIGHT,...: Re-score the blocks or windows offline with these weights (tenths, -50 to 50) instead of the ones the node used: routed, delivered, received; one not named is 0. E.g. --score received=10 judges on what was heard alone. The node's own verdict is unchanged.
beebo analyze tuning indicators

The tuning trial indicators per window (Routed, Delivered, Received, TX Dispatched, RX Invalid, RX Errors, Nbr Heard, Nbr Marginal, Pool Busy, CAD Busy, Noise dBm; counts per minute), rebuilt from the raw records of a downloaded set: summary statistics and one chart per indicator (--table adds the per-window table). The event-based rows (RX Invalid, RX Errors, Pool Busy, CAD Busy) need a raw .bin set; the last three come from route_stats events, which newer firmware logs every minute. Followed by indicator names (indicators received delivered) it shows only their charts; indicator NAME analyzes one in depth.

Arguments

  • [FILE] [INDICATOR]... (optional): Show only the charts of these indicators or chart keys (the key before the colon in a chart title; case, spaces and punctuation ignored, a unique part of the name is enough: 'dispatched' draws the Routed chart). Default: every chart. FILE is as for the other reports.

Options

  • --window, -w MINUTES: Window size in minutes (not seconds) for aggregation. Default 5.
  • --hours H or H1-H2: Restrict samples to this local hour-of-day range (0-23), e.g. '22-6' for overnight (wraps past midnight). Records with no timestamp are dropped. Off (all hours) by default.
  • --json: Print one JSON object per window instead of a table -- for piping into further analysis (e.g. correlating candidate parameter values against these indicators offline). Off by default.
  • --table, --no-table: Also print the per-row table (one row per window or hour; long on multi-day sets). Off by default.
  • --score NAME=WEIGHT,...: Re-score the blocks or windows offline with these weights (tenths, -50 to 50) instead of the ones the node used: routed, delivered, received; one not named is 0. E.g. --score received=10 judges on what was heard alone. The node's own verdict is unchanged.
beebo analyze tuning phi

Noise of the trials' paired score differences: phi and the block spread.

A block's variance is v + c: v the counting noise, 1 / N for N the counts per block of the score weights (the confirmed deliveries for the default weights), c the block spread, the part that longer blocks do not average away, shown as the relative standard deviation sqrt(c) in percent. phi = var(d) / (2 v), d the ln score difference B - A of a pair, is 1 for pure counting noise. Prints each trial, the pool per switch and block length, and the overall pool with 95% intervals: phi, and the spread with the block size N = 1 / c beyond which longer blocks add little. The reference spread is the value the planned trial planner starts from.

Arguments

  • [FILE] (optional): One raw monring_<date>.bin day file. A relative FILE is resolved against --dir. Defaults to the most recently pulled one.

Options

  • --from NAME: Read the file monitor pull --to NAME writes instead of the day files.
  • --json: Print JSON instead of tables.
  • --all: Read every raw set under --dir, not just one.
  • --score NAME=WEIGHT,...: Re-score the blocks or windows offline with these weights (tenths, -50 to 50) instead of the ones the node used: routed, delivered, received; one not named is 0. E.g. --score received=10 judges on what was heard alone. The node's own verdict is unchanged.
beebo analyze tuning trial

Paired A/B result of the latest on-device trial (--all: every trial in the file).

Re-derives the trial's decision from its MON_EVAL blocks with the device's own sequential rule (confidence interval on the paired score difference, confirm ratio within 2 pp, aborts on a guardrail trip) and compares it with the device's MON_TRIAL end record. Blocks are paired (2k, 2k+1). --blocks additionally prints every block in chronological order, to spot drift or an anomaly the paired summary averages away.

Arguments

  • [FILE] (optional): One raw monring_<date>.bin day file. A relative FILE is resolved against --dir. Defaults to the most recently pulled one.

Options

  • --from NAME: Read the file monitor pull --to NAME writes instead of the day files.
  • --json: Print JSON instead of tables.
  • --legend, --no-legend: Print the Definitions table (indicators, columns, verdicts) before the trial(s). Off by default; turn on when you need a reminder of what the columns mean.
  • --blocks: Also print every block in chronological order, one row per block with every indicator -- off by default since a long trial can run to dozens of blocks.
  • --all: Report every trial in the file, not just the latest.
  • --diagnostics: Also chart the diagnostic indicators (TX Dispatched, RX Invalid, ...); by default only the score and its indicators (Routed, Delivered, Received) are charted. The table lists all.
  • --score NAME=WEIGHT,...: Re-score the blocks or windows offline with these weights (tenths, -50 to 50) instead of the ones the node used: routed, delivered, received; one not named is 0. E.g. --score received=10 judges on what was heard alone. The node's own verdict is unchanged.

beebo battery

Battery monitoring and management.

Commands: calibrate, state

beebo battery calibrate

Measure Vbat with a voltmeter and calibrate board.adc.multiplier.

Reads the node's currently-reported battery voltage, asks for a real voltmeter reading at the Vbat JST pigtail (defaulting to the V4.3 regulator's 4.2V charge setpoint, which has measured accurate), and sets board.adc.multiplier so the node's reported voltage matches. With VOLTS the reading is given up front and applied without asking.

Arguments

  • [VOLTS] (optional): The voltmeter reading at the Vbat pigtail. Given, it is applied without prompting (the node console takes the same argument); omit it to be asked.

Options

  • --target VOLTS: Expected Vbat voltage, used as the default voltmeter reading (default 4.2V, the V4.3 regulator's charge setpoint).
  • --yes, -y: Accept --target as the measured reading and apply the new multiplier without prompting -- for non-interactive/cron use.

beebo battery state

Voltage, the raw BATT_STATE_* classifier state from BattTrend.h, and the symbol status node would render -- separate from status node so the classifier state is visible even when it disagrees with the voltage. Give VALUE to force the state instead of just reading it.

Arguments

  • VALUE (optional): Omit to show the current state. Give one of: charging, discharging, charged, plugged, unknown (INIT) to force the BattTrend.h classifier into that state -- useful for testing the status node/battery state symbol without waiting for a real charge/discharge edge. RAM-only, not persisted; the firmware re-seeds the trend anchor to the current voltage so the forced state isn't immediately undone by the next sample.

beebo channels

Manage a companion's group channels and the shared channels.json.

list channels on the node. get CHANNEL one channel's slot, name and secret. set IDX NAME write a slot: '#name' derives a public secret, else --secret HEX or --random; recorded in <root>/mesh/channels.json. remove CHANNEL empty a slot (channels.json keeps the entry). send CHANNEL TEXT send a flood group message (--max-hops N limits how many repeaters forward it). push write channels.json to the node. pull record the node's channels into channels.json.

CHANNEL is a slot index or a name.

Commands: get, list, pull, push, remove, send, set

beebo channels get

Show one channel.

Arguments

  • CHANNEL: Slot index or channel name.

beebo channels list

List the node's channels.

beebo channels pull

Record the node's channels into channels.json.

beebo channels push

Write channels.json to the node.

beebo channels remove

Empty a slot.

Arguments

  • CHANNEL: Slot index or channel name.

beebo channels send

Send a flood group message.

Arguments

  • CHANNEL: Slot index or channel name.
  • TEXT

Options

  • --max-hops MAX_HOPS: Limit the flood to this many repeater hops (1-21; 0 = no limit): the packet is sent with a pre-filled path, so any repeater stops forwarding it.

beebo channels set

Write slot IDX and record it in channels.json.

Arguments

  • IDX
  • NAME

Options

  • --secret SECRET: 16-byte secret, 32 hex digits.
  • --random: Generate a random secret.

beebo check

Health/optimization check-up: a verdict (OK / WARN / FAIL / PENDING) per indicator -- QoS, SoH, TX power, RX/FEM gain, RX capacity, active faults, battery, and tuning-optimizer state -- each with what's "in range" and what to do if it isn't, the way a blood-test report reads rather than a raw numbers dump. Just the verdict table -- for the underlying detail, beebo monitor/beebo monitor decode/beebo analyze tuning already cover it. Settings-derived checks (TX power, RX/FEM gain, BLE PIN, admin password, dedup window, completeness/sanity) are shared with beebo config check -- see config.py's run_settings_checks().

Reads the ring the same way monitor pull does (briefly pausing capture on the firmware side, resumed automatically when it completes) plus the current radio/repeater.routing.tuning.*/monitor settings. Nothing is written to disk or changed on the device. multi_role only (MonRing has no CommonLink/legacy simple_repeater equivalent) -- unlike most of repeater_settings.py's dual-mode leaves, this isn't ported to the repeater's plain-text CLI, so it's rejected the same way any other non-ported command is (see connect.run's raw_ok default).

beebo clock

Show the device's time and drift from host, or set it explicitly.

Arguments

  • TIME (optional): epoch seconds to set the device clock to; omit to read/sync against this host.

Options

  • --boot: If the device is found ahead, reboot it immediately to apply the correction instead of waiting for a future reboot.

beebo config

Save, list, diff, and apply named device configurations.

Commands: backup, check, create, delete, diff, identity, list, onboard, pull, push, recover, show, switch

beebo config backup

Capture a full-device recovery bundle for disaster recovery.

Both roles' private keys and complete settings, gated behind ONE password -- board.node.password, a dedicated backup/recovery credential present on every build. Prompts to set it now if it's never been set (it exists solely for this command, no reason to require a separate prior step); otherwise the device verifies you already know it before unlocking repeater.guest.password/ node.wifi.password/repeater.admin.password too. That same password then encrypts the whole bundle.

Meant for disaster recovery onto new hardware after a HW failure -- NOT a regular config snapshot (see config pull for that). Fails outright (no file written) if the password can't be verified -- there is no partial/unencrypted fallback. The resulting file is saved 0600 under ~/.beebo/devices/<board.name>/recovery/.

Arguments

  • NAME (optional): Recovery bundle name. Defaults to <slug>-<date> if omitted, where slug is board.name if set, else board_id (this node's stable hardware ID) -- unlike node/role names, which can change between backups.

Options

  • --note NOTE: Freeform note describing this backup (e.g. why it was taken). Prompted for interactively if omitted.

beebo config check

Settings sanity/completeness check-up.

Unset fields, sub-optimal or non-compliant values, and drift from best-known defaults -- the settings-only half of beebo check (see that command for telemetry/runtime health instead). Also runs the same out-of-range check config onboard --against uses (default template 'factory'), shown as '[template] <path>' rows -- --fix repairs them the same way onboard --default does. --role checks a specific (possibly non-live) role's own settings instead.

Options

  • --role ROLE: companion or repeater -- check that role's own persisted settings instead of this alias' own role (nodes/<alias>/profile.toml), or whichever is live if this alias has none, the same way config pull/diff/push --role target a non-live role without switching board.node.role first. Not available over a CommonLink connection (-r/--raw), which can only ever reach the live repeater role.
  • --against NAME: Name of the saved configuration (see config pull) to also check settings against, on top of the built-in checks below -- its [ranges] entries flag out-of-range/mangled values, its [defaults] entries are what --fix resets them to. Silently skipped if NAME hasn't been captured yet.
  • --fix: Auto-repair any --against violation that has a template default, instead of only reporting it.

beebo config create

Generate a fresh, purely local named identity (nodes/NAME/).

No device connection -- apply it to a real device later with beebo config switch NAME.

Arguments

  • ROLE: companion or repeater -- the role this new identity is for.
  • NAME: Name for the new node identity -- becomes nodes/<name>/.

Options

  • --copy EXISTING: Clone nodes/<EXISTING>/ (settings configs, contacts, profile.toml connection params -- everything) into nodes/<NAME>/ first, then overwrite just the identity with the new keypair. EXISTING must already have its own identity.json. ROLE still applies to the new identity even if it differs from EXISTING's own role.

beebo config delete

Delete a saved configuration, template, backup, or node identity.

KIND settles outright which of the four namespaces NAME resolves against, since a name can otherwise collide across more than one.

Arguments

  • KIND: 'config' (a config pull/push snapshot), 'template' (a device-agnostic validation template like 'factory'), 'backup' (a config backup recovery bundle), or 'node' (a named identity -- config create/switch). Singular, unlike list's own plural kinds -- delete always removes exactly one item.
  • NAME: Configuration name.

Options

  • --role ROLE: companion or repeater -- disambiguate NAME when it names a role-split template (templates/<role>/NAME.toml, e.g. 'factory') and both roles have one. No effect on a plain config (those aren't role-split). Omit to use this alias' own role if it has one (nodes/<alias>/profile.toml), else companion's copy if present, else repeater's.

beebo config diff

Compare a saved configuration against the connected device.

Only for the paths the file actually records, not the whole settings tree (so this also works cleanly to compare two different physical boards, not just re-check one board against its own earlier snapshot). NAME defaults to <node_name>-<role>. Trailing PATHS restrict which saved paths are compared, even if the file has more (e.g. 'node board'); omit to consider every path in the file. PATHS require NAME to be given explicitly first. --role diffs against a specific (possibly non-live) role instead of whichever is live -- the underlying reads already address the right role's data by path regardless (companion./repeater. opcodes work unconditionally over a binary connection, and node.transport./node.wifi./node.ble.pin route through their own role-targeted opcodes, see settings_tree.py's raw_role/set_role), so --role here mainly affects NAME's default. A saved path whose branch isn't present on the target at all (e.g. every repeater. entry while diffing against a live companion) is reported as one collapsed '<branch>.' row, not one row per leaf.

Arguments

  • NAME (optional): Configuration name. Defaults to <node_name>-<role> (the connected device's own name and the targeted role -- --role if given, else whichever is live) if omitted.
  • PATHS (optional): Dotted settings-tree path prefixes to restrict which saved paths are compared -- e.g. 'node board'. A path matches if it equals a prefix or is nested under one. Omit to consider every path in the file. Requires NAME to be given explicitly first (the first positional argument is always NAME).

Options

  • --role ROLE: companion, repeater, or all -- act on that role's settings instead of this alias' own role (nodes/<alias>/profile.toml), or whichever is live if this alias has none. Both roles' persisted state is reachable over the binary connection regardless of which is live, so this doesn't require switching board.node.role first. Not available over a CommonLink connection (-r/--raw), which can only ever reach the live repeater role. all is only supported by pull/push, and requires NAME to be omitted (each role resolves its own default <node_name>-<role> name).

beebo config identity

Show, import, export, or generate the device's identity.

Options

  • --role ROLE: companion or repeater -- which role's identity to act on. Both roles' identities are reachable over the binary connection regardless of which is currently live, so this can target either one on demand. Defaults to the node's current live board.node.role.

Commands: export, generate, import, save

beebo config identity export

Export the device's current keypair to a file.

In the same format identity import expects.

Arguments

  • FILE (optional): Where to write the exported keypair JSON. Omit to save to BEEBO_ROOT/nodes/<node>/identity.json (same as identity save) -- or <node>/<pubkey[:8]>.json if identity.json is already taken.

Options

  • --role ROLE: companion or repeater -- which role's identity to act on. Both roles' identities are reachable over the binary connection regardless of which is currently live, so this can target either one on demand. Defaults to the node's current live board.node.role.
beebo config identity generate

Generate a brand new Ed25519 identity keypair.

Without --role: purely local, no device connection needed -- generates and saves it to --file (or, without --file, to BEEBO_ROOT/nodes/<node>/<pubkey[:8]>.hash) for a later identity import to apply. With --role: connects and generates the identity ON-DEVICE for that role, overwriting whatever identity it already has -- the private key never leaves the device. Irreversible; identity export --role ... first if it's worth keeping.

Options

  • --file FILE: Where to write the purely-local generated keypair JSON (no --role). Omit to save to BEEBO_ROOT/nodes/<node>/<pubkey[:8]>.hash. Has no effect combined with --role -- the on-device form writes nothing locally.
  • --role ROLE: companion or repeater -- which role's identity to act on. Both roles' identities are reachable over the binary connection regardless of which is currently live, so this can target either one on demand. Defaults to the node's current live board.node.role.
  • --yes, -y: Skip the confirmation prompt.
beebo config identity import

Import a keypair from FILE as the device's identity.

Replacing the identity invalidates every contact's cached shared secret on the device (it reloads contacts after the swap) and changes the hash this node is known by on the mesh — existing peers will see it as a new, unrecognized node until they re-hear its advert.

Arguments

  • FILE: Keypair JSON file ({"public_key": <64 hex>, "private_key": <128 hex>}) to import as the device's identity, replacing the current one. If the file also has "role"/"name" fields (as identity export writes), they become this command's own --role default and are applied to that role's own name after import, unless --role is given explicitly.

Options

  • --role ROLE: companion or repeater -- which role's identity to act on. Both roles' identities are reachable over the binary connection regardless of which is currently live, so this can target either one on demand. Defaults to the node's current live board.node.role.
  • --yes, -y: Skip the confirmation prompt.
beebo config identity save

Shorthand for identity export with no FILE.

Always writes to BEEBO_ROOT/nodes/<node>/identity.json, the node's own directory in the config fs layout -- <node> is the exported role's own name, not necessarily the connection's live node.

Options

  • --role ROLE: companion or repeater -- which role's identity to act on. Both roles' identities are reachable over the binary connection regardless of which is currently live, so this can target either one on demand. Defaults to the node's current live board.node.role.

beebo config list

List saved configs, templates, backups, and/or node identities.

Arguments

  • KIND (optional): Restrict the listing to 'configs' (config pull/push files captured from a specific device), 'templates' (device-agnostic validation templates like 'factory' -- see config pull --factory/config check --against), 'backups' (config backup recovery bundles), or 'nodes' (named identities -- config create/switch). Omit to list all four.

beebo config onboard

Guided, question-driven first-time setup (or review) for a device.

Node name, role, LoRa preset, repeater/guest password, home region, WiFi. Before each role's questions, also runs a sanity/repair pass against the --against template (default 'factory'), catching out-of-range/mangled values the guided questions alone wouldn't (they only ever offer the device's current value as a default, so a bad value would otherwise just be silently re-confirmed) -- prints a summary report of what was found and fixed.

Arguments

  • SAVE_AS (optional): Save the resulting settings as this configuration name (skips the interactive save prompt at the end).

Options

  • --default: Auto-repair any setting found outside the --against template's valid range by applying that template's default, instead of prompting for a replacement value.
  • --against NAME: Name of the saved configuration (see config pull) to validate this role's settings against -- its [ranges] entries flag out-of-range values, its [defaults] entries are what --default (or a bare Enter on the resulting prompt) resets them to. Silently skipped if NAME hasn't been captured yet.

beebo config pull

Snapshot the connected device's current settings into a named file.

NAME defaults to <node_name>-<role> (or just "factory" when --factory is given with no NAME -- see below). Trailing PATHS restrict the capture to matching dotted settings-tree path prefixes (e.g. 'node board repeater.routing'); omit for the whole tree. PATHS require NAME to be given explicitly first. --role captures the non-live role's settings instead; --role all captures both roles into that one file (default name is just <node_name>, no role suffix, since the file already holds both). --factory builds this file's [defaults] from verified firmware constants instead of the capture itself -- see --factory's own help; beebo config pull --factory alone is the common case (no need to also spell out NAME 'factory').

Arguments

  • NAME (optional): Configuration name. Defaults to <node_name>-<role> (the connected device's own name and the targeted role -- --role if given, else whichever is live) if omitted.
  • PATHS (optional): Dotted settings-tree path prefixes to restrict the capture to -- e.g. 'node board repeater.routing'. A path matches if it equals a prefix or is nested under one. Omit for the whole tree. Requires NAME to be given explicitly first (the first positional argument is always NAME).

Options

  • --role ROLE: companion, repeater, or all -- act on that role's settings instead of this alias' own role (nodes/<alias>/profile.toml), or whichever is live if this alias has none. Both roles' persisted state is reachable over the binary connection regardless of which is live, so this doesn't require switching board.node.role first. Not available over a CommonLink connection (-r/--raw), which can only ever reach the live repeater role. all is only supported by pull/push, and requires NAME to be omitted (each role resolves its own default <node_name>-<role> name).
  • --factory: Override [defaults] with verified firmware-compiled defaults (radio freq/bw/sf/cr/txpower/rxgain, FEM rxgain, transport enable flags, dedup window) wherever one is known, instead of mirroring this pull's own captured [values] -- lets this file double as a real known-good template (e.g. beebo config pull factory --factory) even when the connected device isn't itself in a known-good state. Paths with no verified firmware default (names, WiFi SSID, region scoping, ...) still fall back to the captured value.

beebo config push

Apply a saved configuration to the connected device.

Only the settings that actually differ, batched through one SET_PREFS_TLV write where possible. NAME defaults to <node_name>-<role>. --confirm asks per-setting instead of once for the whole batch. Trailing PATHS restrict which saved paths are considered, even if the file has more (e.g. 'node board' applies only those, leaving repeater. untouched). PATHS require NAME to be given explicitly first. --role pushes to a specific (possibly non-live) role instead of whichever is live -- the underlying writes already address the right role's data by path regardless (companion./repeater. opcodes work unconditionally over a binary connection, and node.transport./node.wifi./node.ble.pin route through their own role-targeted opcodes, see settings_tree.py's raw_role/set_role), so --role here mainly affects NAME's default. --role all pushes both roles' shares of one merged file (see config pull --role all), one role at a time. A saved path whose branch isn't present on the target at all (e.g. every repeater. entry while pushing to a live companion) is reported as one collapsed '<branch>.*' row, not one row per leaf, in the diff shown before applying.

Arguments

  • NAME (optional): Configuration name. Defaults to <node_name>-<role> (the connected device's own name and the targeted role -- --role if given, else whichever is live) if omitted.
  • PATHS (optional): Dotted settings-tree path prefixes to restrict which saved paths are applied -- e.g. 'node board'. A path matches if it equals a prefix or is nested under one. Omit to consider every path in the file. Requires NAME to be given explicitly first (the first positional argument is always NAME).

Options

  • --confirm: Ask y/N for each individual changed setting instead of one confirmation for the whole batch.
  • --role ROLE: companion, repeater, or all -- act on that role's settings instead of this alias' own role (nodes/<alias>/profile.toml), or whichever is live if this alias has none. Both roles' persisted state is reachable over the binary connection regardless of which is live, so this doesn't require switching board.node.role first. Not available over a CommonLink connection (-r/--raw), which can only ever reach the live repeater role. all is only supported by pull/push, and requires NAME to be omitted (each role resolves its own default <node_name>-<role> name).

beebo config recover

Restore a full-device recovery bundle onto the connected device.

Decrypts it (prompting for the same board.node.password used in config backup), imports both roles' private keys, applies every captured setting, and re-sets every captured password.

Meant for a brand-new/replacement board recovering a HW-failed node's identity and configuration -- imports REPLACE this device's identity for each role in the bundle, same peers-impact caveat as config identity --import (existing peers stop recognizing this node under that role until they re-hear its advert, unless the board being recovered onto is genuinely new and never had its own).

Arguments

  • NAME: Recovery bundle name, or a bare board.name/board_id to auto-pick its newest bundle (see config backup).

Options

  • --yes, -y: Skip confirmation prompts.

beebo config show

Print a saved configuration's values.

Arguments

  • NAME: Configuration name.
  • PATH (optional): Dotted settings path to narrow the display to, e.g. companion.radio or repeater.radio.lora.freq -- matches that path or anything nested under it. Omit to show everything in the file.

Options

  • --role ROLE: companion or repeater -- disambiguate NAME when it names a role-split template (templates/<role>/NAME.toml, e.g. 'factory') and both roles have one. No effect on a plain config (those aren't role-split). Omit to use this alias' own role if it has one (nodes/<alias>/profile.toml), else companion's copy if present, else repeater's.

beebo config switch

Apply a saved named identity (nodes/NAME/) to the connected board.

Imports the keypair, sets the role's name, switches role if needed, and reboots -- all in one atomic server-side call. NAME becomes the alias to use for this board going forward, not whichever alias was used to perform the switch.

Arguments

  • NAME: Node identity to apply to the connected board -- nodes/NAME/identity.json must already exist (see config create).

Options

  • --yes, -y: Skip the confirmation prompt.

beebo connect

Connect the REPL's device session, using whichever transport was already resolved at beebo -i ... invocation unless one is typed here.

Options

  • --ble, -b ADDRESS: Connect via BLE to ADDRESS ('scan' to discover). Default: scan.
  • --tcp, -t HOST: Connect via TCP to HOST[:port]. Default: beebo.
  • --usb, -u PORT: Connect via USB serial PORT. Default: /dev/ttyACM0.
  • --raw, -r: Force the plain-text CommonCLI protocol instead of the companion binary protocol -- named for the protocol, not the node's live role (either role answers over either protocol on multi_role).

beebo contacts

Show or refresh the local contact cache.

Mirrors the node's contacts into an editable JSON file (contacts.json) so commands like beebo rxlog can name neighbors and compute their distance from us — offline included. A received hash is matched as a prefix of each contact's public key (full precision, 1-3 bytes). Direct neighbors are folded in (hops 0); Beebo itself is included. When a short prefix is ambiguous, the nearest contact (by cached location) wins.

(no args) list the cached contacts (offline). refresh re-query the node and merge into the cache. active TARGET flag a node reachable, or unreachable with --off.

Options

  • --name, -n NAME: Only contacts whose name contains TEXT.
  • --hash, -H PREFIX: Only contacts whose public key starts with hex PREFIX.
  • --type, -t TYPES: Only these node types (comma list: chat, rep, room, sens).
  • --neighbors, -N: Only direct (zero-hop) neighbors.
  • --active, --inactive: Only reachable / only inactive contacts.
  • --located: Only contacts with advertised coordinates.
  • --near KM: Only contacts within KM of self.
  • --collisions: Only contacts whose 1, 2, or 3-byte public-key prefix collides with another contact's — see the 1B/2B/3B columns.
  • --sort-by FIELDS: Sort the table by a comma list of fields, each breaking ties in the one before, drawn from name, hops, in_hops, heard, active, dist, pubkey, 1b, 2b, 3b, 1B, 2B, 3B (lowercase 1b/2b/3b sort by received-hash prefix; uppercase 1B/2B/3B sort by that column's win/tie/lose/no-collision mark). Default: name.
  • --group-by FIELDS: Draw a rule between runs of rows sharing the same value for these fields (comma list, same choices as --sort-by), nested left to right (e.g. 1b,2b draws a line at every 2B change, and also at 1B changes); always sorted ahead of --sort-by so the runs stay contiguous.
  • --resolve-by RULES: The criteria used to resolve a colliding hash prefix to one hash/name: a comma list of rules, tried in order, drawn from active, heard, distance. A rule that doesn't apply to a candidate (e.g. no known location, for distance) is skipped rather than counted as a tie. Default: active,heard,distance.
  • --limit, -L N: Show only the first N rows after sorting/grouping (0 for no limit). Default: 20.

Commands: active, free-1b, refresh, remove

beebo contacts active

Flag a cached node reachable (default) or inactive (--off). (c)

A purely local annotation on the cache — no node connection needed. An inactive node still resolves when a received hash matches it unambiguously, but loses any ambiguous short-prefix match to an active candidate, even a farther one (see :meth:Resolver._compare). The flag survives refreshes.

Arguments

  • TARGET: Contact name (exact or prefix) or public-key hex prefix.

Options

  • --off: Flag the node inactive/unreachable instead.

beebo contacts free-1b

List 1-byte (2 hex char) prefixes no cached contact's public key starts with. (c)

Scans the offline cache only. Handy for picking a hash prefix guaranteed not to collide with any contact currently resolved — see the 1B column for the reverse view (which contacts already share a 1-byte prefix).

beebo contacts refresh

Re-query the node and merge into contacts.json, then list only what changed. (c)

Fetches every contact's name and advertised location plus our own node's location, so neighbor names and distances stay current, plus each one's 'in hops' (one bulk BEEBO_CMD_GET_ADVERT_PATHS round trip, matched locally -- see :func:_apply_in_hops) wherever the firmware's advert-path cache still has it. The result is merged over the existing cache, not a replacement: contacts the node has since evicted from its bounded store are kept, so the cache only grows. The listing at the end only shows entries that are new or whose fields actually changed by this refresh — on a large, mostly-stable cache the full table is mostly noise repeating what you already saw last time; beebo contacts (no subcommand) still shows everything, unfiltered.

Options

  • --limit, -L N: Show only the first N changed rows after sorting/grouping (0 for no limit). Default: 20.

beebo contacts remove

Delete saved contacts from the node (companion role). (c)

Targets are matched against the node's own contact list, not the local cache, so it reaches every contact -- unlike neighbors remove, which only sees direct neighbors. Removed contacts also leave contacts.json. The node re-adds a contact the next time it hears its advert, unless manual contact adding is on.

Arguments

  • TARGETS (optional): Contact name (exact or prefix) or public-key hex prefix; several allowed. A target matching more than one contact aborts the command before anything is removed.

Options

  • --all: Remove every contact on the node.
  • --yes, -y: Do not ask before --all removes everything.

beebo discover

Probe the mesh for nodes and show responders type and SNR.

Arguments

  • FILTER (optional): Node types to discover: 'all', an int mask, or a comma list (cli, rep, room, sens).

Options

  • --full: Request full 32-byte public keys (default: prefix).
  • --timeout, -T TIMEOUT: Seconds of silence that ends collection. Default: 5.0.
  • --json: Emit the raw responses as JSON.
  • --resolve-by RULES: The criteria used to resolve a colliding hash prefix to one hash/name: a comma list of rules, tried in order, drawn from active, heard, distance. A rule that doesn't apply to a candidate (e.g. no known location, for distance) is skipped rather than counted as a tie. Default: active,heard,distance.

beebo measure

RF bench measurements: noise floor, BW sweep, TX-power curves.

Commands: cw, noise, rssi-by-bw, txpower

beebo measure cw

Key a manual CW carrier at one TX power level for bench measurement.

Options

  • --power, -p POWER: TX power in dBm (e.g. -9..22).
  • --secs, -s SECS: Carrier duration in seconds; 0 keeps it on until Ctrl-C (no firmware auto-stop). Default: 30.

beebo measure noise

Characterize the receiver noise floor across rxgain/fem.rxgain configs.

Arguments

  • TEST: Which noise-floor procedure to run.

Options

  • --antenna-ref: Keep the node's FEM RSSI offsets: values at the antenna connector, not raw SX1262 input.

beebo measure rssi-by-bw

Sweep LoRa bandwidth (50 ohm stub) to see how the noise floor scales.

beebo measure txpower

Sweep TX power and read peaks on a spectrum analyzer (advert or CW).

Options

  • --carrier, -c advert|cw: Carrier type: advert bursts (max-hold) or a steady CW tone. Default: Carrier.advert.
  • --optimize, --no-optimize: CW only: paOptTable (optimize) vs fixed PA / monotone low end.

beebo messages

Download pending messages, or send a new one.

Commands: download, send

beebo messages download

Download all pending messages from the node and save them to disk.

Loops CMD_SYNC_NEXT_MESSAGE until the node reports no more messages, so it drains the full queued backlog across every channel and direct contact. Every message is tagged with the radio's current rxgain and fem.rxgain and carries rxlog-comparable SNR / RSSI / path fields for reception analysis.

Options

  • --format, -f csv|json: Output format for the drained messages. Default: Format.csv.
  • --output, -o PATH: Write to PATH instead of a timestamped file under the device's data dir.
  • --limit, -n N: Stop after N messages (0 = drain the whole backlog).

beebo messages send

Send a direct text message to a contact and report whether it was ACK'd or timed out.

Without --flood/--direct, uses the library's send_msg_with_retry (same as a normal message send -- multiple attempts, automatically falls back to flood after repeated direct failures). With --flood or --direct, does exactly one controlled attempt in the requested mode instead, so a test send's route is exactly what was asked for rather than whatever the node's own path cache and retry logic would otherwise pick. Either way, success/failure here is exactly what increments this node's own ack_success_count/ack_timeout_count, and a flood send that gets re-transmitted back into earshot is what increments echo_success_count (beebo status packets).

--direct --force additionally pre-loads a known-good zero-hop path onto the device (for a contact already cached as a direct neighbor) before sending, so the first attempt goes direct instead of needing an earlier round trip to teach the device the path.

Arguments

  • TARGET (optional): Contact name or public-key hex prefix.
  • TEXT (optional): Message text.

Options

  • --flood: Force flood routing for this one send -- resets the node's known path to this contact first (CMD_RESET_PATH), so the send can't silently use an already-known direct path. Lets you deliberately exercise the flood-echo side (echo_success_count) instead of waiting for organic flood traffic.
  • --direct: Force direct routing -- fails clearly if the node has no known path to this contact yet, rather than silently falling back to flood. Exercises the DM-ACK side (ack_success_count/ack_timeout_count) specifically over a direct route.
  • --force: With --direct, only for a contact already cached as a known zero-hop neighbor (out_path_len 0): write that zero-hop path onto the device first (CMD_ADD_UPDATE_CONTACT, no round trip needed since an empty path is unambiguous), so the send goes direct on the first attempt instead of needing the device to (re)learn the path. Refused for any contact that isn't already a known zero-hop neighbor -- a multi-hop out_path is an opaque next-hop id the mesh must hand us for real, not something safe to fabricate.

beebo monitor

Health and service monitoring management and dashboard.

Commands: capture, clear, debug, decode, events, list, plot, pull, start, status, stop, tuning, watch

beebo monitor capture

Show or set the persisted MonRing capture config — shorthand for beebo settings monitor.<leaf> <on/off> without the 'settings monitor.' prefix.

Arguments

  • LEAF (optional): Which capture bit: enabled/rx/tx/radio/env/batt/tuning/event/setting/command/route/debug/path (setting/command/route/debug alias event -- same bit). Omit to show all of them.
  • VALUE (optional): 'on' or 'off' to set the leaf. Omit to read.

beebo monitor clear

Clear the monitoring ring.

beebo monitor debug

Stream live firmware debug/monitor-ring events, replaying the ring's current backlog first.

Arguments

  • KINDS (optional): Filter to specific kinds: sync, rx, tx, batt, command, debug, env, event, radio, route, setting, adaptive, eval, trial -- plus H, M, L to narrow debug-kind events to those severities (implies the debug kind even if not also named). No KINDS shows everything.

Options

  • --out, -o TARGET: Stream destination: "-" for console (default), else a file path.

beebo monitor decode

View, filter, and optionally save the locally pulled monitor-ring dataset (written by monitor pull) -- purely offline, no device connection. Selects a date range from the pulled dataset for the current device: --start/--end, defaulting (both absent) to just the latest locally pulled day. Further narrowed by --kind/--where, decoded and filtered in full; --limit is applied last, after every other filter, and always keeps the newest N regardless of --desc/--asc -- or, with --around POS, a window of --limit records centered on the (filtered) record nearest POS instead of the newest N. Display order is oldest-first (--asc) by default; --desc reverses it.

--debug: renders debug (kind == debug) records as a chronological narrative -- time/delta/severity/file:line/event/detail -- for root-causing a specific incident, instead of the generic table below. Implies --kind debug (any other --kind is ignored); incompatible with --save/--name.

--start/--end syntax: [YYYY-MM-DD-]HH[:MM[:SS]] -- a time-of-day, defaulting to today when no date is given; the date (given or defaulted) picks which day file(s) to read, the time narrows the kept records to an exact instant within (and across) those days: --start 17:10 --end 18:00 today, 17:10 to 18:00 --start 2026-08-01-22 --end 2026-08-02-06 22:00 to 06:00, one span A bare YYYY-MM-DD (no time) still covers the whole day -- midnight for --start, end-of-day for --end. Omit either for no bound in that direction (with both omitted, only the latest pulled day is read).

--where syntax: COLUMN<op>VALUE (op is ==, !=, >=, <=, >, < or a bare = for ==; ~ is a case-insensitive substring match), combined with and/or (case-insensitive, and binds tighter, like Python) and grouped with parens: --where "(snr>=5 and kind=rx) or (snr<=4 and kind=tx)" --where setting=tx_power --where nbr~f5 Quote expressions with spaces/parens. >=/<=/>/< need both sides numeric, so e.g. an snr condition drops non-rx rows. Multiple --where flags AND together on top of this.

--save/--name: writes CSV/JSON in the layout monitor analyze reads (one file per radio profile, under ~/.beebo/nodes/<node>/data/<profile>/): --save bare flag, auto-named monitor_<tag>.csv --name NAME.json NAME.json picks the name and format, implies --save --name always wins the format (csv/json); --save alone defaults to csv.

rx rows (kind == rx) columns: pos — causal-order index (sort key across the whole set) time — absolute capture time (resolved via the nearest sync record) snr — SNR in dB rssi — RSSI in dBm route — FLOOD or DIRECT (from the packet header) type — payload type (from the packet header): REQ, RESPONSE, TEXT_MSG, ACK, ADVERT, GRP_TXT, GRP_DATA, ANON_REQ, PATH, TRACE, MULTIPART, CONTROL, RAW_CUSTOM (beebo's poke sub-protocol), or RESERVED_12/13/14 (unused) nbr — neighbor/relay id, hex hops — path hash count on the wire (0 = heard directly, before any relay appended its hash -- same check the direct-neighbor table uses); empty/unknown on a capture from before MonRing ABI version 2 verdict — terminal local outcome for this node (see verdict below) routing — relay outcome for this node (see routing below) invalid — the frame's header/path did not parse (see invalid below) pkt_hash — packet hash, hex; correlates rx/tx/duplicated across records

verdict (what this node did with it, as the packet's destination -- independent of routing below, which is what this node did as a relay; a packet can carry both, neither, or either alone): captured — record captured, but no local verdict was ever reached (e.g. a channel message relayed without being able to decrypt it against any channel this node holds) accepted — decrypted and delivered to us duplicated — already seen (duplicate), dropped not_for_us — dest-hash mismatch, or decrypt/MAC failed wrong_hop — direct route, we are not the next hop filtered — rejected by the flood receive filter incomplete — payload too short for its type forged_sig — advertisement signature failed to verify self — our own advert echoed back to us unknown_type — payload type we do not handle parse_err — bad version / path / corrupt / oversize pool_full — no free packet buffer to receive into misc_drop — dropped with no more specific reason

routing (what we did as a relay -- independent of verdict above, which is what we did as the packet's destination; a packet can carry both, neither, or either alone): terminated — no relay action taken (duplicate, delivered/consumed here with no next hop, or we weren't the addressed next hop) -- see verdict for why forwarded — retransmit scheduled (repeater role) no_forward — forwarding disabled, so not relayed path_full — wanted to relay but the path-hash buffer was full

invalid (rider): the frame's header/path did not parse (malformed or truncated); route, nbr and hash are unknown.

tx rows (kind == tx) columns: pos — causal-order index (sort key across the whole set) time — absolute capture time route/type — as above, for the transmitted packet dst — next-hop neighbor id, hex (DIRECT only; empty for FLOOD) airtime_ms — estimated airtime of the transmission, ms (at its own coding rate) attempt — 0 for the first send, n for the nth route retry of the same packet cr — LoRa coding rate 4/x (5 to 8) the frame went out at result — outcome of our own transmission (see result below) ack — DIRECT confirmation verdict: success/timeout/overflow, joined in from this file's own EVENT_ACK_ record by pkt_hash (empty if unresolved within this file) echo — FLOOD confirmation verdict: success/timeout/overflow, joined in from this file's own EVENT_ECHO_ record the same way. For a retried packet the verdict belongs to the attempt it names; the attempts before it show timeout pkt_hash — packet hash, hex; correlates rx/tx/duplicated across records

result: ok — sent normally timeout — send timed out (e.g. channel busy, radio failure)

radio rows (kind == radio; one row per config change, not per packet) columns: pos — read-order index; sorts ahead of the RX/TX rows that depend on this config time — absolute capture time freq/sf/bw/cr — LoRa frequency (MHz) / spreading factor / bandwidth (kHz) / coding rate, in force from this point forward tx_power — TX power, dBm rxgain, lna — as above

env rows (kind == env; one row per environment sample, not per packet) columns: pos — read-order index; sorts ahead of the RX/TX rows that depend on this sample time — absolute capture time noise_floor — noise floor in dBm temp_c — temperature, C (vbat lives in batt rows below, not here -- see MON_BATT/BattRecord; free_heap/pool_free/tx_queue/err_flags/cad_busy_events/CPU pct don't have a MonRing home as of 2026-09-07 -- see MonRing.h's EnvRecord comment for why, and STATS_TYPE_CORE/STATS_TYPE_SYSTEM or beebo status resources for a live current-value read of most of them)

batt rows (kind == batt; one row per Vbat sample) columns: pos — read-order index time — absolute capture time vbat — battery voltage, mV idle — sample taken with the radio idle (IR-drop-clear) vs forced at deadline state — BattTrend classifier state: unknown/stable/charging/discharging/charged/plugged 24g — BLE and/or TCP powered at sample time (~75mA idle draw each; independent of usb, since USB can be up at the same time) usb — USB powered at sample time

sync rows (kind == sync; one row per time-base epoch, not per packet) columns: pos — read-order index; the oldest sync always sorts first time — the absolute base timestamp for the epoch it opens

Options

  • --kind, -k sync|rx|tx|radio|env|batt|adaptive|event|setting|command|route|debug|eval|trial: Record kind(s) to keep (default: all). Repeatable.
  • --where EXPR: Filter expression, see above. Repeatable (ANDed). Default: [].
  • --start [YYYY-MM-DD-]HH[:MM[:SS]]: Earliest day/instant to include, see above. Default: none (with --end absent too, only the latest pulled day file is read).
  • --end [YYYY-MM-DD-]HH[:MM[:SS]]: Latest day/instant, see above. Default: none (with --start absent too, only the latest pulled day file is read).
  • --limit, -n LIMIT: Max records to keep -- always the newest N, regardless of --desc/--asc (or, with --around, the window size centered on that position). 0 = all. Default: 20 when printing, all with --save. Printing (not --save) errors out past 1000 records -- narrow the selection or use --save instead.
  • --around POS: Center the kept set on record POS instead of keeping the newest --limit: half the window before POS, half after (applied after --kind/--where/--start/--end; POS need not itself survive those filters -- the nearest surviving pos is used). 0 for --limit means the whole filtered set, same as without --around.
  • --desc, --asc: Newest-first or oldest-first (default). --limit always keeps the newest N regardless of this.
  • --save: Save instead of printing a table, see above.
  • --name NAME.csv|NAME.json: Implies --save. NAME picks the filename stem and format (csv/json) instead of the auto date/duration tag.
  • --from NAME: Read the file monitor pull --to NAME writes instead of the day files.
  • --split: With --save, also write one file per kind.
  • --debug: Render debug (kind == debug) records as a chronological narrative -- time/delta/severity/file:line/event/detail -- instead of the generic table. Implies --kind debug; incompatible with --save.

beebo monitor events

Show or set per-EVENT_TYPE MonRing capture -- a finer filter under monitor capture event (that toggle gates MON_EVENT capture at all; this picks which event TYPES within it are actually kept). Capture is not free (DYNAMIC_OPTIMIZER_PLAN.md) and a busy node can log a lot of tx_ack_success/tx_echo_success records -- exclude the noisy ones you don't need without losing every other event type's history.

Persisted per role, same as monitor capture's per-kind bits (GET/SET_PREFS_TLV(KEY_MONRING_EVENT_MASK); formerly a RAM-only mask via the individual GET/SET_MONRING_EVENT_MASK opcodes, resetting to capture-everything on every reboot -- now survives a reboot like every other MonRing capture setting).

Arguments

  • NAME (optional): Event name (Event Catalog name, e.g. tx_ack_success) to toggle. Omit to show every event type's capture state.
  • VALUE (optional): 'on' or 'off' to set. Omit to just read.

beebo monitor list

List locally pulled monring_<YYYY-MM-DD>.bin files for the current device, with a record count and a per-kind breakdown for each -- so you can see what's actually in a day's file before decodeing it. Purely offline, no device connection.

beebo monitor plot

Live charts of the monitor stream.

Commands: rssi, tuning

beebo monitor plot rssi

Live RSSI of received packets per neighbor, with the noise floor. The table lists each neighbor's last RSSI, its average over the window, its best since the start, its packets in the window and its rate per minute, with a line for all packets. Move the antenna and watch the last/average columns settle. Ctrl-D or Ctrl-C quits.

Options

  • --neighbor, -n HEX: Plot only neighbors whose id starts with HEX; repeat for several. Without it, the --top most heard.
  • --top TOP: Neighbors to plot when no --neighbor is given, the most heard first; 0 = all.
  • --span, -s SECONDS: Width of the rolling window. Shorter reacts faster to antenna moves. Default: 60.
  • --grid: Draw a dim dotted grid at the axis ticks.
beebo monitor plot tuning

Live charts of the tuning: the indicators per window, the running trial.

Commands: indicators, trial

beebo monitor plot tuning indicators

Live tuning indicators, the charts of analyze tuning indicators updated by each event. Starts empty and fills as records arrive, like plot rssi; --replay fills it from the ring. Ctrl-D or Ctrl-C quits.

Arguments

  • [INDICATOR]... (optional): Charts to draw, as in analyze tuning indicators: an indicator or chart key (case, spaces and punctuation ignored, a unique part of the name is enough), or all. Default: Score.

Options

  • --span, -s SECONDS: Width of the rolling window. Shorter reacts faster. Default: 60.
  • --window, -w SECONDS: Trailing time each point summarizes: the rates are per minute over this long. Shorter reacts faster, longer is smoother. Default: 15.
  • --grid: Draw a dim dotted grid at the axis ticks.
  • --replay: Fill the chart from the ring first. The whole ring replays oldest first, minutes on a full one, so it only helps a long --span.
  • --score NAME=WEIGHT,...: Re-score the blocks or windows offline with these weights (tenths, -50 to 50) instead of the ones the node used: routed, delivered, received; one not named is 0. E.g. --score received=10 judges on what was heard alone. The node's own verdict is unchanged.
beebo monitor plot tuning trial

Live view of the setting trial: running block and side, the A B B A strip, the paired statistics so far and a chart of A and B per pair, refreshed from the monitor ring. Ctrl-D or Ctrl-C quits.

Arguments

  • [INDICATOR]... (optional): Indicators to chart, as in analyze tuning indicators: Score or a row of analyze tuning trial (case, spaces and punctuation ignored, a unique part of the name is enough), or all. Default: Score.

Options

  • --switch, -s SWITCH: Follow this switch's trial. Without it, the running trial, else the latest.
  • --every SECONDS: Ring re-read period; the countdown moves every second. Default: 30.
  • --score NAME=WEIGHT,...: Re-score the blocks or windows offline with these weights (tenths, -50 to 50) instead of the ones the node used: routed, delivered, received; one not named is 0. E.g. --score received=10 judges on what was heard alone. The node's own verdict is unchanged.

beebo monitor pull

Pull the monitoring ring's raw wire-format records into the local per-node dataset (monring_<YYYY-MM-DD>.bin under ~/.beebo/nodes/<node>/data/), incrementally by default.

This is the day-to-day command for keeping an offline dataset in sync with minimal transport traffic -- monitor decode reads back whatever this writes (to view, filter, and optionally convert to CSV/JSON for analyze), without touching the device.

Each calendar day's records land in their own file, split per-record from its own decoded capture time, not once per pull -- a first-ever pull of a ring that already spans several days writes several files in one call; a routine incremental poll normally writes one (today's), or two if the batch happens to straddle midnight. Every file is self-contained (a synthetic SYNC/RADIO/ENV context record is prepended when a file doesn't already start with one of its own), so any subset of days decodes correctly on its own. --incremental (default) resumes from monring.cursor.json's remembered seq/bytes when still verifiably valid; a device reboot or ring clear since the last pull is detected and recorded as a gap in the cursor rather than silently mis-resuming. Only plain binary connections work here (the default, even to a repeater-role node) -- an explicit -r/--raw (text-CLI) session can't run the binary MonRing protocol at all, so it's rejected.

Options

  • --incremental, --full: Resume from the previous pull's cursor (default): only fetches records newer than what's already on disk. --full forces a complete re-fetch from seq 0, e.g. to rebuild the local dataset from scratch.
  • --clear: Clear the ring on the device once the pull (and file write) has completed, so the next pull starts a fresh window.
  • --to NAME: Append to NAME.bin in the node's data dir instead of the day files, with its own resume cursor (NAME.cursor.json); the first pull takes everything the ring holds. Read it back with --from NAME.

beebo monitor start

Resume ring capture — convenience for beebo settings monitor.enabled on.

beebo monitor status

Lifetime drop/fault counters, traffic and time shares.

Commands: health, rates, service

beebo monitor status health

Health Status: the lifetime fault and drop counters (what SoH is made of).

beebo monitor status rates

Service Rates: busy and idle time shares, loops per second and the worst loop stall.

beebo monitor status service

Service Status: traffic, duplicate and delivery counters with their per-minute rates.

beebo monitor stop

Pause ring capture — convenience for beebo settings monitor.enabled off.

beebo monitor tuning

State of the trial and adaptive tuners: settings, latest outcomes.

Commands: adaptive, trial

beebo monitor tuning adaptive

The adaptive (bandit) tuner: its settings and the latest proposal the ring holds for each parameter (old > proposed, whether it was applied, the reward before the step).

Options

  • --json: Print JSON instead of tables.
beebo monitor tuning trial

The setting trial: show its state, start or stop it.

Options

  • --json: Print JSON instead of tables.

Commands: block_count, block_size_s, combined, confidence, disable, enable, gain, min_pkt_rate, profile, score, start, stop, values

beebo monitor tuning trial block_count

Show or set the number of blocks per trial [0 = auto, 2 to 1000] (tuning.trial.block_count). Setting is refused while the trial is on: stop it first.

Arguments

  • [VALUE] (optional): New value. Omit to show the current one.
beebo monitor tuning trial block_size_s

Show or set the block length in seconds [0 = auto, 1 to 3600] (tuning.trial.block_size_s). Setting is refused while the trial is on: stop it first.

Arguments

  • [VALUE] (optional): New value. Omit to show the current one.
beebo monitor tuning trial combined

Show or set whether one combined trial runs instead of one per switch (tuning.trial.combined). Setting is refused while the trial is on: stop it first.

Arguments

  • [VALUE] (optional): New value: on or off. Omit to show the current one.
beebo monitor tuning trial confidence

Show or set the trial confidence level, percent [90, 95 or 99] (tuning.trial.confidence_pct). Setting is refused while the trial is on: stop it first.

Arguments

  • [VALUE] (optional): New value. Omit to show the current one.
beebo monitor tuning trial disable

Take switches out of the sequence (their enable bit off), leaving the others as they are. Refused while the trial is on: stop it first.

Arguments

  • SWITCH...|ALL: Switches by name (rxboost, lna, agc, interference, cad, cr, multiacks, retry, rxdelay, txdelay, directtxdelay, airtime), or 'all'.
beebo monitor tuning trial enable

Add switches to the sequence (their enable bit on), leaving the others as they are. Refused while the trial is on: stop it first. start turns it on.

Arguments

  • SWITCH...|ALL: Switches by name (rxboost, lna, agc, interference, cad, cr, multiacks, retry, rxdelay, txdelay, directtxdelay, airtime), or 'all'.
beebo monitor tuning trial gain

Show or set the smallest worthwhile score gain, percent [0 to 50] (tuning.trial.min_gain_pct). Setting is refused while the trial is on: stop it first.

Arguments

  • [VALUE] (optional): New value. Omit to show the current one.
beebo monitor tuning trial min_pkt_rate

Show or set the least counts per minute a trial may run on [0 to 255], 0 = none (tuning.trial.min_pkt_rate). Setting is refused while the trial is on: stop it first.

Arguments

  • [VALUE] (optional): New value. Omit to show the current one.
beebo monitor tuning trial profile

Show or set the trial profile (tuning.trial.profile). Setting is refused while the trial is on: stop it first.

Arguments

  • [VALUE] (optional): New value: main (the LNA, CAD, TX delay and retry switches, one challenger each) or exhaustive (every switch, full value lists). Omit to show the current one.
beebo monitor tuning trial score

Show or set the score weights (tuning.score.*) the trial judges blocks with: routed (packets routed per hour), delivered (the share of them confirmed) and received (valid packets heard per hour). Defaults 10, 10, 0 give the confirmed deliveries. Setting is refused while the trial is on: stop it first.

Arguments

  • [INDICATOR] (optional): routed, delivered or received. Omit to show all three weights.
  • [WEIGHT] (optional): New weight in tenths, -50 to 50 (10 = 1.0, 0 = ignored). Omit to show the current one.
beebo monitor tuning trial start

Start the trial sequence: the named switches (their enable bits set, the others cleared), 'all', or whatever is still pending. Refused while the trial is already on: stop it first. A running or queued trial still waits for the RX rate gate. In tuning.trial.start list mode the sequence first stores each selected switch's A value. RAM-only: a reboot leaves it off.

Arguments

  • [SWITCH...|ALL] (optional): Switches to run, by name (rxboost, lna, agc, interference, cad, cr, multiacks, retry, rxdelay, txdelay, directtxdelay, airtime), or 'all'. Without any, the switches still pending run.
beebo monitor tuning trial stop

Stop the trial: sets tuning.trial.enabled off, which aborts a running trial and restores its original value. The enable bits stay, so start resumes the pending switches.

beebo monitor tuning trial values

Show or set the values a switch's trial explores: A first, then the challengers, in the setting's own units. values default [SWITCH|all] resets lists to the built-in ones. Setting is refused while the trial is on: stop it first.

Arguments

  • SWITCH|DEFAULT: The switch, or 'default' to reset lists.
  • [A,B,...|SWITCH|ALL] (optional): Values to try: A first, then 1 to 3 challengers (the switch's own units, e.g. 5,6,7,8 for cr). With 'default': the switch to reset, or 'all'. Omit to show the list.

beebo monitor watch

Like monitor debug, but skip the ring's replay-on-enable backlog walk -- only show events from here on.

Arguments

  • KINDS (optional): Filter to specific kinds: sync, rx, tx, batt, command, debug, env, event, radio, route, setting, adaptive, eval, trial -- plus H, M, L to narrow debug-kind events to those severities (implies the debug kind even if not also named). No KINDS shows everything.

Options

  • --out, -o TARGET: Stream destination: "-" for console (default), else a file path.

beebo neighbors

List and manage the node's direct neighbors.

Auto-add is usually off on a busy mesh (too many nodes), so an immediate neighbor we hear over the air often isn't in the saved contact list. The firmware keeps a small RAM-only table of every node heard via a zero-hop advert (see MyMesh::onAdvertRecv); this command streams it out over the companion link (CMD_GET_NEIGHBORS 138) so we can see who our real neighbors are — with name, SNR, advertised location and distance.

The advert carries the neighbor's full identity, so unlike a relayed packet's one-byte path hash this list is unambiguous and locatable. beebo contacts refresh merges these neighbors into the resolver cache and promotes each fresh one to a node contact (see :func:promote_neighbor), so the node's own contact list — and beebo rxlog — keep up with who it actually hears.

beebo neighbors # list cached direct neighbors (offline) beebo neighbors pull # re-query the node and update neighbors.json beebo neighbors discover # actively query for repeaters, then list beebo neighbors discover -t sensor -t room # query several node types at once beebo neighbors discover --resolve # also fetch names for nameless hits beebo neighbors discover --passive # old behavior: advert + passive listen beebo neighbors resolve <id> # request a nameless neighbor's name/owner beebo neighbors probe <id> # zero-hop reachability probe of a neighbor beebo neighbors update # complete partial entries from mesh/directory.json (asks first) beebo neighbors promote <id> # save one neighbor as a node contact beebo neighbors remove <id> # delete that contact from the node beebo neighbors forget <id> # prune one entry from the node's own RAM table

<id> matches a neighbor by hash prefix (hex, any length) or by name.

Commands: discover, forget, peek, ping, poke, probe, promote, pull, remove, resolve, update

beebo neighbors discover

Actively query nearby nodes for their identity, then list what answered.

Sends a NODE_DISCOVER_REQ (zero-hop broadcast, protocol.yaml) rather than waiting on a node's own periodic self-advert -- only nodes matching --type answer, tagged so we only count replies to this request. Answers (and any advert heard meanwhile) land in the node's direct-neighbor table the same way a passive sighting does, so beebo neighbors/pull see them too. --type sensor/room finds nodes this firmware itself can never BE (multi_role only ever answers as chat or repeater) but can still discover elsewhere on the mesh -- e.g. a simple_sensor device answering its own NODE_DISCOVER_REQ handler.

--passive restores the original behavior (announce ourselves, wait for others' own adverts) for a node too old to have BEEBO_CMD_NODE_DISCOVER.

Options

  • --type, -t TYPE: Node type(s) to query for: repeater/sensor/room/chat/all (repeat for several, e.g. -t repeater -t sensor). Default: repeater.
  • --prefix-only: Ask responders to reply with only an 8-byte pubkey prefix instead of the full key (smaller/faster, but not enough to promote a sensor/room hit to a saved contact).
  • --timeout, -T TIMEOUT: Seconds to wait for responses before listing what came in. Default: 6.0.
  • --passive: Old behavior: send our own zero-hop advert and passively wait for others' adverts, instead of actively querying them.
  • --resolve: After listing, also req_owner (see 'beebo neighbors resolve') any full-key hit that came back without a name -- up to 8 per run.

beebo neighbors forget

Prune one or more entries from the direct (zero-hop) neighbor table itself -- distinct from remove, which deletes a saved contact. This is RAM-only and re-populates the next time that node's advert is heard directly; mirrors CommonCLI's text-CLI 'neighbor.remove'.

IDENT may be a comma-separated list -- all entries are resolved against one consistent snapshot up front, then each is forgotten in turn. A plain miss or a forget failure on one item is reported and skipped rather than aborting the rest of the list; an AMBIGUOUS item (matches more than one neighbor) aborts the whole command instead, before anything is forgotten -- safer than guessing which one you meant partway through a batch.

Arguments

  • IDENT: Neighbor(s) to forget, comma-separated: hash prefix (hex, any length) and/or name. A prefix that matches more than one neighbor is rejected -- give more of the hash (the 'pubkey' column) to disambiguate.

beebo neighbors peek

Show exactly the node's live RAM neighbor table, nothing more and nothing carried over from neighbors.json -- useful right after a firmware change to the neighbor-matching logic, to see the table's actual current contents without the cache's own merge history in the way. Does not touch neighbors.json; see beebo neighbors pull to merge into the persisted cache instead.

beebo neighbors ping

Measure a link with repeated direct trace round-trips.

Without --precision the run stops at --pongs successes (or --pings attempts, whichever comes first). With --precision set, --pongs becomes a floor: the run continues past it until the SNR mean converges to that precision (or --pings is exhausted).

Arguments

  • TARGET (optional): Contact name or public-key hex prefix (optionally hex:flags).

Options

  • --converge, -C: Preset for a 'let the mean stabilize' run: sets sensible defaults --precision 0.5, --reject 3.5, --pongs 8 and --pings 100 so you don't have to. Any of those passed explicitly still wins.
  • --pongs, -s INTEGER: Stop once this many attempts succeed. Default: 5.
  • --pings, -a INTEGER: Maximum number of attempts. Default: 10.
  • --timeout, -t SECONDS: Per-attempt timeout in seconds (0: firmware suggestion).
  • --interval, -i SECONDS: Wait this long before sending each subsequent ping (0: send the next as soon as the previous resolves).
  • --precision, -p DB: Stop once the SNR mean is known to within ±DB. Each pong is a noisy SNR sample; the mean's uncertainty is the standard error SEM = stddev/sqrt(n), and the 95% confidence half-width is t(n-1)*SEM. The Student-t factor t(n-1) is used instead of the normal 1.96 because the stddev is estimated, not known: at small n it inflates the interval (e.g. ~2.36 at n=8 vs 1.96), so a tiny sample can't falsely look precise, and it tends to 1.96 as n grows. With --precision the run continues until that half-width is <= DB for both far and near SNR (so the means won't drift more than ~DB with further sampling); a floor of 5 samples just ensures the variance is defined. Unlike --pongs (a fixed count), this samples just long enough to pin the mean to the precision you ask for: a clean link finishes early, a noisy one keeps going up to --pings. 0 disables it (use --pongs). Typical: 0.5.
  • --reject, -r Z: Drop SNR outliers from the stats using the robust modified z-score Z = |x - median| / (MAD/0.6745), where MAD is the median absolute deviation (the median and MAD are used, not mean/stddev, so the outliers can't hide themselves). A sample is rejected when its far or near SNR exceeds Z; 3.5 is the standard Iglewicz-Hoaglin cutoff (~3.5 robust sigma). Rejected samples are excluded from the mean, CI and convergence but still shown (marked 'rejected') and kept in the CSV. Needs at least 5 samples; 0 disables it. Note: a deep fade may be real link behavior, so use with care. Typical: 3.5.
  • --flag-inactive: If the node can't be reached, flag it inactive (unreachable) in the contact cache. Off by default — inactive is a manual call (beebo contacts active --off); a reachable node is always flagged active regardless.

beebo neighbors poke

Poke a neighbor for its own RSSI/SNR/Noise-Floor.

A direct-route round trip to a known direct neighbor (no flood, no route discovery) — same requirement as a direct trace with a cached route.

Arguments

  • TARGET (optional): Contact name or public-key hex prefix.

Options

  • --pokes, -n COUNT: Number of pokes to send, one row per reply. Default: 1.
  • --timeout, -t SECONDS: Reply timeout in seconds (0: firmware suggestion).

beebo neighbors probe

Probe a node with a zero-hop request to test direct reachability.

Sends the lightest round-trip MeshCore has — an anonymous BASIC request (just the remote clock) — forced zero-hop, so only a node we can reach directly answers. A reply means a direct connection is possible; the SNR and RSSI we heard the reply at are reported (inbound direction only — a BASIC reply carries no far-side metric). A last_probe timestamp (and the measured snr) is always written to the cache.

A single zero-hop request can drop on a marginal link, so the probe retries up to --tries times and stops at the first reply.

Reachability is only the return path: a busy repeater that ignores BASIC sync looks silent even though we hear its traffic, so by default the probe leaves the cache active flag untouched. Pass --flag to write the flag from the outcome — the signal the rxlog resolver uses to break a hash-prefix collision (the active one wins) — only when you trust the probe for these targets.

A neighbor is by definition direct/zero-hop, so targets come straight from neighbors.json (no promotion to a node contact needed -- see :func:_resolve_owner for why: this builds the same library-local, zero-hop synthetic contact that req_basic_sync's send_anon_req() needs, never touching the node's own contact list). With no argument every full-key neighbor in the cache is probed in one pass; run beebo neighbors pull first if the cache is stale/empty.

Arguments

  • IDENT (optional): Neighbor/contact to probe: hash prefix (hex) or name. Omit to probe every known direct neighbor.

Options

  • --tries, -n TRIES: Zero-hop attempts before declaring unreachable. Default: 5.
  • --timeout, -T TIMEOUT: Per-attempt reply timeout in seconds (0: firmware suggestion).
  • --flag, --no-flag: Write the cache active flag from the probe outcome (default: report only). A silent probe does NOT mean a dead node — a busy repeater ignores BASIC sync though we hear it fine — so flagging is opt-in.

beebo neighbors promote

Save a direct neighbor as a node contact.

beebo contacts refresh promotes every fresh neighbor automatically; this is the explicit, single-neighbor form.

Arguments

  • IDENT: Neighbor to save: hash prefix (hex) or name.

beebo neighbors pull

Re-query the node's direct-neighbor table, merge it into neighbors.json, and list the merged view.

The cache only grows (see refresh_cache()'s docstring), so this listing can include entries this particular pull didn't actually re-hear. now= recomputes every row's age from its stored heard (see _list()) rather than trusting each entry's frozen age field, which would otherwise make an old carried-over entry look freshly-heard just because it happens to still be in the merged list.

See beebo neighbors peek for the node's live RAM table only, without touching or merging into the cache.

beebo neighbors remove

Delete a (previously promoted) neighbor's contact from the node.

IDENT may be a comma-separated list -- all entries are resolved against one consistent snapshot up front, then each is removed in turn. A plain miss or a remove failure on one item is reported and skipped rather than aborting the rest of the list; an AMBIGUOUS item (matches more than one neighbor) aborts the whole command instead, before anything is removed -- safer than guessing which one you meant partway through a batch.

Arguments

  • IDENT: Neighbor/contact(s) to remove, comma-separated: hash prefix (hex, any length) and/or name. A prefix that matches more than one neighbor is rejected -- give more of the hash (the 'pubkey' column) to disambiguate.

beebo neighbors resolve

Actively request a neighbor's name/owner string (req_owner).

A NODE_DISCOVER_RESP or a zero-hop sighting with a stripped advert sometimes carries a hash but no name; this asks that specific node "who are you" directly, the same follow-up the phone app's discovery tool offers. Uses the neighbor's known-direct route straight from neighbors.json -- no node contact list involved.

Arguments

  • IDENT: Neighbor to resolve: hash prefix (hex) or name.

Options

  • --timeout, -T TIMEOUT: Seconds to wait for the reply. Default: 5.0.

beebo neighbors update

Complete partial neighbors.json entries from the mesh node directory (BEEBO_ROOT/mesh/directory.json, downloaded from cascadiamesh.org).

Only entries known by a key prefix are considered. One is completed when its prefix matches exactly one directory node and that node is within --max-km of this node; name, type and location fill the blanks. The proposals are shown first and nothing is written until you confirm. Offline, no device connection.

Options

  • --max-km KM: Only accept a directory node within KM of this node. Default: 100.0.
  • --yes, -y: Apply without asking.
  • --dry-run: Show the proposed updates and stop.
  • --origin LAT,LON: This node's location for the distance check. Default: the location neighbors pull recorded in neighbors.json, else contacts.json.

beebo ota

Upload a firmware image to the node over the air.

Arguments

  • FIRMWARE: Firmware .bin image to upload.

Options

  • --priority, -p, --no-priority: Ask the node to skip radio dispatch for the duration of the transfer, so OTA chunks aren't delayed sharing loop() with mesh/radio work. Honored on every transport (BLE/USB/TCP) -- live mesh traffic can throttle OTA throughput several times over regardless of which transport is carrying the transfer. On by default; pass --no-priority to keep servicing the radio during the transfer.

beebo profile

Command runtime and latency profiling.

Options

  • --raw: Print the raw firmware per-event timeline instead of aggregating (firmware ring only -- the host log has no fetch-boundary marker to correlate a raw timeline against).
  • --sort COLUMN: Sort the merged table by COLUMN (command, source, count, min, avg, max, total). Numeric columns sort biggest-first; command/source sort alphabetically. Ignored with --raw. Default: avg.
  • --reverse: Flip --sort's default direction.
  • --limit N: Show only the first N rows after sorting; 0 shows all. Ignored with --raw. Default: 20.

Commands: clear

beebo profile clear

Reset both the firmware ring and the host-side log to empty.

Useful before a study session so beebo profile only reflects commands run during that session, not stale events accumulated since boot (or since the last clear).

beebo reboot

Reboot the node and synchronize time.

beebo region

Region management for repeater flood scoping.

Named-region registry for repeater flood scoping (opt-in). 'beebo settings repeater.node.region.home/.list' picks/lists; this group defines the tree. 'repeater.node.region.default' is the setting that tags this repeater's own outgoing traffic (RegionMap's own independent default_id -- distinct from repeater.node.region.home's cosmetic home_id bookmark, and from companion.node.region.scope, companion's own separate field).

When a region-tagged flood packet reaches a repeater, only three outcomes exist: the tag matches a flood-allowed region -- passes; it matches a flood-denied region -- dropped; it matches no region this repeater has defined at all (e.g. a stray 'WA' it never 'region put') -- also dropped, unrecognized is deny-by-default, not pass-through (removing regions doesn't help here -- only defining the missing one does). A genuinely untagged packet (no repeater.node.region.default set anywhere, the common case) skips matching entirely and always passes, via the wildcard root's own default -- that's the only real 'off switch', and it lives on the sender (repeater.node.region.default none), not on this repeater. There's no toggle to disable filtering here without changing the tree: 'region allowf <name>' clears a deny flag, 'region put <name>' defines a missing one -- there's no single flag on the repeater to turn filtering off wholesale short of 'repeater.repeat off', which stops all relaying, not just region-filtered flood.

Only flood-routed packets are ever filtered this way -- direct traffic is untouched.

The parent/child hierarchy ('region put <name> <parent>') is cosmetic bookkeeping only, for the tree view and to stop 'region remove' on a region that still has children -- it has no effect on matching. Each region is checked independently by its own name-derived key and its own allow/deny flag; a child never inherits its parent's flag, and denying a parent doesn't cascade to anything nested under it.

Commands: allow, def, deny, put, remove, save, tree

beebo region allow

Allow flood traffic tagged with this region to be relayed.

Arguments

  • NAME: Region name.

beebo region def

Bulk-define a region subtree in one command (CommonCLI's text-CLI 'region def').

Arguments

  • SEGMENTS: Space-separated segments, each 'name' or 'name|jump'/'name,jump': define 'name' under the current cursor (wildcard root at the start), then move the cursor to 'jump' (an existing region, matched by name prefix) or stay at 'name' if no jump is given. Example: 'west pnw|pdx wa|kls or wv,sle' builds a subtree in one round trip instead of one 'region put' per region.

beebo region deny

Deny (stop relaying) flood traffic tagged with this region.

Arguments

  • NAME: Region name.

beebo region put

Define a region, or re-parent an existing one -- new regions default to flood-allowed.

Arguments

  • NAME: Region name to define or re-parent.
  • PARENT (optional): Parent region name (omit for the root).

beebo region remove

Remove a region -- must have no child regions.

Arguments

  • NAME: Region name to remove.

beebo region save

Explicitly persist the region tree to flash. Every other 'region' subcommand already auto-persists on success -- this is a no-op in practice, kept for backward compatibility with CommonCLI's text-CLI 'region save', which some callers send out of habit after 'put'/'def'.

beebo region tree

Show the region hierarchy. Color: yellow = repeater.node.region.default (the region a packet actually departs with), green = flood-allowed, red = flood-denied. A trailing ★ marks RegionMap's own separate 'home' bookmark, if set -- cosmetic, never consulted by any forwarding decision.

beebo release

Disconnect the REPL's device session -- or, if a reconnect is currently in flight after an unexpected drop (t.session is None but t.reconnect_task isn't -- see _watch_disconnect()/_reconnect_loop() above), cancel that instead of erroring.

beebo run

Curated and user-provided use-case scripts.

Options

  • --list: List available scripts instead of running one.

Commands: api-demo, api-sync-demo, calibrate-vbat

beebo run api-demo

Connect via beebo.api.Node and print identity/status/battery.

Pass exactly one of --ble/--tcp/--usb/--node to select a device, e.g. beebo run api-demo --tcp bench. A minimal, runnable demonstration of the beebo.api module for general Python scripting.

Options

  • --ble ADDRESS: Connect via BLE ('scan' to discover).
  • --tcp HOST: Connect via TCP (host[:port]).
  • --usb PORT: Connect via USB serial port.
  • --node ALIAS: Connect via a configured node alias (~/.beebo/nodes/<alias>/profile.toml).

beebo run api-sync-demo

Connect via beebo.api.SyncNode and print identity/status/battery.

Pass exactly one of --ble/--tcp/--usb/--node to select a device, e.g. beebo run api-sync-demo --node bench-mc. Add --peer bunch-mc for a two-node message round trip. The blocking counterpart to beebo run api-demo.

Options

  • --ble ADDRESS: Connect via BLE ('scan' to discover).
  • --tcp HOST: Connect via TCP (host[:port]).
  • --usb PORT: Connect via USB serial port.
  • --node ALIAS: Connect via a configured node alias (~/.beebo/nodes/<alias>/profile.toml).
  • --peer ALIAS: Second node alias -- adds a message round trip from the first node to this one.
  • --text TEXT: Message body for the round trip. Default: ping.

beebo run calibrate-vbat

Measure Vbat with a voltmeter and calibrate board.adc.multiplier.

Reads the node's currently-reported battery voltage, asks for a real voltmeter reading at the Vbat JST pigtail (defaulting to the V4.3 regulator's 4.2V charge setpoint, which has measured accurate), and sets board.adc.multiplier so the node's reported voltage matches. With VOLTS the reading is given up front and applied without asking.

Arguments

  • [VOLTS] (optional): The voltmeter reading at the Vbat pigtail. Given, it is applied without prompting (the node console takes the same argument); omit it to be asked.

Options

  • --target VOLTS: Expected Vbat voltage, used as the default voltmeter reading (default 4.2V, the V4.3 regulator's charge setpoint).
  • --yes, -y: Accept --target as the measured reading and apply the new multiplier without prompting -- for non-interactive/cron use.

beebo rxlog

Capture received packets and summarize their SNR / RSSI.

Plain: listens to the node's live RX feed and reports per-metric stats, broken down per transmitting neighbor. It stops once every neighbor with enough samples has its mean SNR converged to --precision (so it quits when more packets stop changing any link); sparse neighbors don't block the stop. --duration forces a fixed window instead; --precision 0 runs until Ctrl-C. --compare: alternates the FEM LNA on/off through the session and runs Welch's t-test on the two groups, so you can tell whether the LNA's effect on SNR/RSSI is real rather than drift. It stops once both groups' SNR means have converged to --precision (after a few toggle cycles). Saves raw rows plus a stats sidecar (unless --no-csv), tagged with TX power and LNA state.

The readings "type" column is the MeshCore payload type of each packet: one of REQ, RESPONSE, TEXT_MSG, ACK, ADVERT, GRP_TXT, GRP_DATA, ANON_REQ, PATH, TRACE, MULTIPART, CONTROL (or UNK for unrecognized), decoded by the meshcore library from the packet header.

Options

  • --duration, -d SECONDS: Fixed capture window in seconds. Overrides convergence; 0 (default) runs until the stats converge (or Ctrl-C).
  • --precision, -p DB: Stop once the mean SNR is known to within ±DB (95% CI half-width) — more packets won't move it. In compare mode both LNA groups must reach it. 0 disables convergence (run until Ctrl-C). Default: 0.5.
  • --noise-precision, -N DBM: Plain mode: also wait until the mean noise floor is known to within ±DBM (95% CI half-width) before stopping. 0 ignores the noise floor when deciding convergence. Default: 0.5.
  • --reject, -r Z: Drop SNR outliers from the stats using the robust modified z-score Z = |x - median| / (MAD/0.6745). Flagged packets are excluded from the mean/CI/convergence but still shown (marked) and kept in the CSV. Needs ≥5 samples; 0 (default) disables it. Note: with mixed sources a low SNR is usually a genuine distant node, not an error, so enable only deliberately (3.5 is the standard cutoff).
  • --compare, -c: Toggle fem.rxgain on/off each interval and Welch-test the two groups at the end (quantifies the LNA's effect).
  • --interval, -i PACKETS: Compare mode: packets received between fem.rxgain toggles. Toggling by packet count (not time) keeps the on/off sample sizes balanced regardless of traffic rate. Default: 30.
  • --resolve, -R, --no-resolve: Resolve each one-byte neighbor hash to a name (and distance from our node) using the JSON cache (neighbors.json); built from the node on first use, then the file is the source of truth (edit names there directly). On by default; --no-resolve falls back to the raw hash only. The cache is built from the node on first use; refresh it later with beebo neighbors pull.
  • --group, -g, --no-group: Plain mode: group the live readings by neighbor — a rolling window of the most recent --rolling packets per neighbor, with the most-recently-heard neighbor on top — instead of one chronological stream.
  • --rolling, -k N: Per-neighbor rolling window size when --group is on. Default: 6.
  • --csv, --no-csv: Write the captured packets to a CSV (and a stats sidecar).
  • --analyze, -A CSV: Offline: re-run the analysis on a previously saved rxlog CSV instead of capturing live (no device connection). Compare vs plain is detected from the file; per-state dwell time (for the reception rate) is reconstructed from packet timestamps. Honors --reject; --resolve uses the neighbors.json cache.

beebo settings

Show or set a node preference by dotted path.

Settings form a tree; only leaves hold a value. Run with no path to see the whole tree, a category path to see one subtree, or a leaf path to read or write that one setting. commit/restore are settings subcommands: they flush or discard whatever console.autosave has been holding in RAM.

The whole-tree view shows only the branches the node's board.node.role can act on: a companion hides repeater., a repeater hides companion.. Naming a hidden branch explicitly ('beebo settings repeater') still shows it.

-h/--help on this command always shows this general text, no matter what path you pass (Click resolves it before the path argument is even parsed). For a leaf's or category's own description instead, use -a/--about, e.g. 'beebo settings -a board.battery' — purely local, no device connection.

console.* covers both the connected node's autosave flag and this CLI's own ~/.beebo/nodes/<node>/profile.toml (console.transport, console.log_level, console.dbg_level, console.ble.address, console.tcp.host, console.usb.port) — the latter never open a transport to read or write.

<role>.node.transport.ble/tcp/usb are the role's own persisted transport enables (which radios come up at boot).

<role>.node.wifi.ssid/password are the role's WiFi station credentials. They're set independently of each other; the password is write-only and never reads back. <role>.node.wifi.ip is the STA's current run-time IP, read-only and never persisted — empty when WiFi is off or not yet associated.

<role>.node.ble.pin is the BLE pairing PIN override — 0 (or empty) falls back to the build-time BLE_PIN_CODE macro, otherwise a 6-digit number. Applied live on a companion node, but only for new pairings: an already-paired client keeps its bond until it unpairs. A repeater node needs a reboot.

<role>.monitor.* is the persisted monitor-ring capture config (enabled, rx, tx, radio, env, batt, tuning, event, ...).

repeater. covers repeater-only CommonCLI prefs with no companion_radio opcode equivalent, nested to mirror CommonCLI's own dotted keys (repeater.node.repeat, repeater.node.name, repeater.node.allow.read.only, repeater.node.advert.interval, repeater.node.advert.flood_interval, repeater.guest.password, repeater.node.owner.info). advert.interval and advert.flood_interval are presence/broadcast policy (how often this node announces itself, at local vs flood scope), not forwarding/routing behavior, so both stay under repeater.node.advert. rather than repeater.routing.. Everything that actually shapes forwarding/routing behavior lives under repeater.routing. instead: timing/collision-avoidance/ duty-cycle knobs and the tuning optimizer's own switches (rxdelay, airtime, dutycycle, txdelay, direct.txdelay, agc.reset.interval, interference.threshold, tuning.adaptive., tuning.score., tuning.trial.*), plus flood-routing behavior (loop.detect, flood.max/max_advert/max_unscoped) — see the "Dynamic Repeater Tuning" doc page.

board.adc.multiplier works for both companion (dedicated binary opcode) and repeater (CommonCLI adc.multiplier) nodes, so it lives outside repeater.*.

beebo settings show the whole tree beebo settings companion.radio show the companion radio subtree beebo settings companion.radio.lora.freq show the freq leaf beebo settings companion.radio.lora.freq 868 set freq to 868 MHz beebo settings companion.radio.lora.preset "USA/Canada" apply a named preset quadruplet beebo settings console.transport tcp change the CLI's default transport beebo settings companion.node.transport.tcp off disable the node's TCP transport beebo settings --reboot companion.node.transport.ble on enable ble and reboot immediately beebo settings companion.monitor.rx off stop capturing rx records beebo settings commit flush in-RAM prefs to flash beebo settings restore discard in-RAM prefs, reload flash

Arguments

  • PATH (optional): Dotted setting path, e.g. companion.radio.lora.freq, or 'commit'/'restore'. Omit to show the whole tree.
  • VALUE (optional): New value for a leaf. Omit to read.

Options

  • --reboot: Reboot immediately to apply the change, instead of the leaf's default (live-apply or wait-for-next-reboot). Only valid when setting a leaf that supports it, e.g. 'beebo settings --reboot node.transport.ble on'.
  • --about, -a: Show this leaf's or category's help text instead of its live value -- purely local, no device connection needed. E.g. 'beebo settings -a board.battery'.

beebo status

Runtime node, radio, resource status.

Options

  • --timing: Print per-command round-trip latency to diagnose which query dominates.

Commands: node, radio, resources

beebo status node

Identity (name, role, model, version, build, transport), uptime, battery, MCU temp.

Options

  • --timing: Print per-command round-trip latency to diagnose which query dominates.

beebo status radio

Noise floor, last RSSI/SNR, TX/RX airtime.

Options

  • --timing: Print per-command round-trip latency to diagnose which query dominates.

beebo status resources

Heap/PSRAM/flash usage, MCU temp, contacts, pending messages, MonRing state.

Options

  • --timing: Print per-command round-trip latency to diagnose which query dominates.

beebo trace

Trace the path to a target, printing each hop's SNR.

Arguments

  • TARGET: Contact name, public-key hex prefix, or raw path (hex[:flags] or comma list).

beebo traffic

Inject flood group traffic on a channel.

Commands: send, sustain

beebo traffic send

Send flood group messages on CHANNEL at a constant rate (Ctrl-C stops).

Keeps a tuned node's counted events (confirmed deliveries for the default score) above tuning.trial.min_pkt_rate while the mesh is quiet. Needs a companion connection with the channel set (see channels).

Arguments

  • CHANNEL: Slot index or channel name.

Options

  • --rate, -r RATE: Packets per minute.
  • --size SIZE: Text length, 6-100. Default: 32.
  • --duration, -d DURATION: Stop after this many seconds.
  • --count, -n COUNT: Stop after this many packets.
  • --max-hops MAX_HOPS: Limit the flood to this many repeater hops (1-21; 0 = no limit): the packet is sent with a pre-filled path, so any repeater stops forwarding it.

beebo traffic sustain

Have a companion node sustain an RX rate on CHANNEL by itself.

The node sends flood group text at random times, only while the packets it hears per minute are below --rate. RAM-only: a reboot stops it. With no arguments, shows the current state.

Arguments

  • CHANNEL (optional): Slot index or channel name.

Options

  • --rate, -r RATE: Target RX packets per minute, 1-255.
  • --off: Stop the injector.