mirror of
https://github.com/yellowcooln/meshcore-mqtt-live-map.git
synced 2026-04-20 23:23:36 +00:00
135 lines
11 KiB
Markdown
135 lines
11 KiB
Markdown
# Repository Guidelines
|
||
|
||
Current version: `1.8.4` (see `VERSIONS.md`).
|
||
|
||
## Project Structure & Module Organization
|
||
- `backend/app.py` wires FastAPI routes, MQTT lifecycle, and websocket broadcast flow.
|
||
- `backend/config.py` centralizes env configuration.
|
||
- `backend/state.py` holds shared in-memory state + dataclasses.
|
||
- `backend/decoder.py` contains payload parsing, multibyte MeshCore decoding, and route helpers.
|
||
- `backend/los.py` contains LOS math + elevation fetch helpers.
|
||
- `backend/history.py` handles 24h route history persistence/cleanup.
|
||
- `backend/static/index.html` is the HTML shell + template placeholders.
|
||
- `backend/static/styles.css` holds all UI styles.
|
||
- `backend/static/app.js` holds all client-side map logic.
|
||
- `backend/static/sw.js` is the PWA service worker.
|
||
- `backend/requirements.txt` and `backend/Dockerfile` define Python and Node dependencies.
|
||
- `docker-compose.yaml` runs the service as `meshmap-live`.
|
||
- `data/` stores persisted state (`state.json`), route history (`route_history.jsonl`), role overrides (`device_roles.json`), and optional neighbor overrides (`neighbor_overrides.json`).
|
||
- `.env` holds dev runtime settings; `.env.example` mirrors template defaults.
|
||
- `VERSION.txt` tracks the current version (now `1.8.4`); append changes in `VERSIONS.md`.
|
||
|
||
## Build, Test, and Development Commands
|
||
- `docker compose up -d --build` rebuilds and restarts the backend (preferred workflow).
|
||
- `docker compose logs -f meshmap-live` follows server logs for MQTT activity.
|
||
- `curl -s http://localhost:8080/snapshot` checks current device state.
|
||
- `curl -s http://localhost:8080/stats` shows ingest/route counters.
|
||
- `curl -s http://localhost:8080/debug/last` inspects recent decoded packets.
|
||
|
||
## Coding Style & Naming Conventions
|
||
- Python in `backend/*.py` uses **2-space indentation**; keep it consistent.
|
||
- The project enforces an **80-character column limit** for Python code to maintain readability.
|
||
- Formatting is handled by `yapf` using the config in `backend/.style.yapf`.
|
||
- To format code manually: `yapf --in-place --recursive --style backend/.style.yapf backend/`
|
||
- HTML/CSS/JS in `backend/static/index.html` uses 2 spaces as well.
|
||
- Use lowercase, underscore-separated names for Python variables/functions.
|
||
- Prefer small helper functions for parsing/normalization; keep logging concise.
|
||
|
||
## Testing Guidelines
|
||
- Run automated tests with `pip install -r requirements-dev.txt && pytest -q`.
|
||
- Validate runtime behavior manually with `/snapshot`, `/stats`, and `/debug/last`.
|
||
|
||
## Commit & Pull Request Guidelines
|
||
- No git history is available in this workspace, so there is no established commit convention.
|
||
- If you add git later, use short, imperative commit messages and describe behavioral changes in PRs.
|
||
|
||
## Configuration & Operations
|
||
- Most behavior is controlled by `.env` (MQTT host, TLS, topics, TTLs, map start lat/lon/zoom, MQTT online window, default map layer).
|
||
- Current dev defaults: `DEVICE_TTL_HOURS=96`, `PATH_TTL_SECONDS=172800`, `MQTT_ONLINE_SECONDS=600`, `ROUTE_TTL_SECONDS=60`, `TRAIL_LEN=0`, `DISTANCE_UNITS=mi`.
|
||
- Node size default is `NODE_MARKER_RADIUS` (pixels); users can override via the HUD slider.
|
||
- History link size default is `HISTORY_LINK_SCALE`; users can override via the History panel slider.
|
||
- Map radius filter: `MAP_RADIUS_KM=0` disables filtering; `.env.example` uses `241.4` km (150mi). Applies to nodes, trails, routes, and history edges.
|
||
- `MAP_RADIUS_SHOW=true` draws a debug circle centered on `MAP_START_LAT/LON`.
|
||
- Set `TRAIL_LEN=0` to disable trails entirely; the HUD trail hint is removed when trails are off.
|
||
- Coverage button only appears when `COVERAGE_API_URL` is set.
|
||
- `QR_CODE_BUTTON_ENABLED=true` adds a `Generate QR Code` button to node popups that opens a theme-aware MeshCore-compatible contact QR modal; default is off.
|
||
- Geographic filtering defaults to radius mode; polygon mode is optional via `MAP_BOUNDARY_MODE=polygon` and `MAP_BOUNDARY_FILE`.
|
||
- Standalone boundary builder: `tools/map-boundary-builder.html` outputs the JSON consumed by `MAP_BOUNDARY_FILE`; hosted copy: [https://yellowcooln.com/map-boundary-builder/](https://yellowcooln.com/map-boundary-builder/).
|
||
- Radar country-bounds controls:
|
||
`WEATHER_RADAR_COUNTRY_BOUNDS_ENABLED` and
|
||
`WEATHER_RADAR_COUNTRY_LOOKUP_URL`.
|
||
`WEATHER_RADAR_COUNTRY_LOOKUP_URL` defaults to
|
||
`/weather/radar/country-bounds` and is an HTTP route path, not a
|
||
filesystem directory.
|
||
- Weather wind controls:
|
||
`WEATHER_WIND_ENABLED`, `WEATHER_WIND_API_URL`,
|
||
`WEATHER_WIND_GRID_SIZE`, `WEATHER_WIND_REFRESH_SECONDS`.
|
||
- LOS curvature controls:
|
||
`LOS_CURVATURE_ENABLED` and `LOS_CURVATURE_FACTOR`.
|
||
When unset, LOS curvature defaults to `true` with factor `1.333333`.
|
||
- `DEVICE_COORDS_FILE` points to optional coordinate overrides (default `/data/device_coords.json`).
|
||
- `NEIGHBOR_OVERRIDES_FILE` can point at a JSON map/list of neighbor pairs to resolve hash collisions.
|
||
- Auto-neighbor overrides are controlled by:
|
||
`AUTO_NEIGHBOR_OVERRIDES_ENABLED`, `AUTO_NEIGHBOR_OVERRIDES_FILE`,
|
||
`AUTO_NEIGHBOR_ACTIVE_DAYS`, `AUTO_NEIGHBOR_MIN_EDGE_COUNT`, and
|
||
`AUTO_NEIGHBOR_REFRESH_SECONDS`.
|
||
- Optional custom HUD link appears when `CUSTOM_LINK_URL` is set.
|
||
- Update banner uses `GIT_CHECK_ENABLED` (compare local vs upstream) with `GIT_CHECK_PATH` pointing at a git repo.
|
||
- `GIT_CHECK_FETCH` controls whether the server fetches before comparing; `GIT_CHECK_INTERVAL_SECONDS` sets the recheck interval.
|
||
- Route history modes default to `path` via `ROUTE_HISTORY_ALLOWED_MODES`.
|
||
- `ROUTE_PATH_MAX_LEN` caps oversized path-hash lists (prevents bogus long routes).
|
||
- `ROUTE_ALLOW_AMBIGUOUS_ONE_BYTE_FALLBACK=true` restores pre-`v1.7.0` closest/time-based fallback for colliding 1-byte route prefixes; default is conservative `false`.
|
||
- Persisted state in `data/state.json` is loaded on startup; edit with care.
|
||
- After editing `backend/*.py` or `backend/static/*`, rebuild with `docker compose up -d --build`.
|
||
- History tool visibility is not persisted; it always loads off unless `history=on` is in the URL.
|
||
|
||
## Feature Notes
|
||
- MQTT supports WSS/TLS or TCP; the official `@michaelhart/meshcore-decoder` runs via a Node helper for advert/location parsing, 1/2/3-byte path decoding, and conservative MQTT role extraction.
|
||
- Routes are rendered as trace/message/advert lines with TTL cleanup; 0,0 coords (including stringy zeros) are filtered from trails/routes.
|
||
- Route IDs are observer-aware (`message_hash:receiver_id`) so multi-observer receptions do not overwrite each other.
|
||
- Dev route debug: in non-prod mode (`PROD_MODE=false`), clicking a route line logs hop-by-hop details to the browser console (distance, hashes, origin/receiver, timestamps).
|
||
- Turnstile protection only activates when `PROD_MODE=true` and requires
|
||
`TURNSTILE_ENABLED`, `TURNSTILE_SITE_KEY`, and `TURNSTILE_SECRET_KEY`.
|
||
- Turnstile browser auth (`meshmap_auth`/`?auth=`) is used for map + WebSocket sessions; protected API routes still require `PROD_TOKEN`.
|
||
- Discord/social embeds can be allowlisted under Turnstile via
|
||
`TURNSTILE_BOT_BYPASS` and `TURNSTILE_BOT_ALLOWLIST`.
|
||
- Route hash collisions prefer known neighbors (and optional overrides); long path lists are skipped via `ROUTE_PATH_MAX_LEN`.
|
||
- Route collisions fall back to closest-hop selection and drop hops beyond `ROUTE_MAX_HOP_DISTANCE`.
|
||
- `ROUTE_INFRA_ONLY` restricts route lines to repeaters/rooms (companions still show as markers).
|
||
- Heatmap shows recent traffic points (TTL controlled).
|
||
- LOS uses `/los/elevations` for client-side realtime updates (with `/los` fallback).
|
||
- LOS uses Earth-curvature-aware obstruction checks by default in both the frontend realtime path and the backend `/los` fallback.
|
||
- LOS UI includes peak markers, a relay suggestion marker, elevation profile hover, and map-line hover sync.
|
||
- LOS legend items (clear/blocked/peaks/relay) are hidden until the LOS tool is active.
|
||
- Mobile LOS supports long-press on nodes (Shift+click on desktop); endpoints can be dragged or click-selected and moved via map click.
|
||
- MQTT online status is derived from `/status` + `/internal` TTL windows; `/packets` is tracked as feed activity.
|
||
- Devices that remain MQTT-online keep their last known coordinates on the map until MQTT presence expires, even if fresh location packets stop.
|
||
- `MQTT_ONLINE_FORCE_NAMES` (comma-separated device names) forces selected nodes to always appear MQTT online.
|
||
- Service worker fetches navigations with `no-store` to avoid stale UI/env toggles (e.g., radius debug ring).
|
||
- Node search + labels toggle (persisted in localStorage) and a GitHub link in the HUD.
|
||
- Hide-nodes toggle hides markers, trails, heat, routes, and history layers.
|
||
- Heat toggle hides the heatmap; it defaults on and the button turns green when heat is off.
|
||
- History line weight was reduced for a lighter map overlay.
|
||
- HUD logo uses `SITE_ICON`; if missing/invalid it falls back to a small "Map" badge to keep the toggle usable.
|
||
- Route styling now keys off payload type: 2/5 = Message (blue), 8/9 = Trace (orange), 4 = Advert (green).
|
||
- 24h route history persists to `data/route_history.jsonl`, renders as a volume heatline, and defaults off (History tool panel).
|
||
- History tool opens a right-side panel with a 5-step heat filter slider: All, Blue, Yellow, Yellow+Red, Red; legend swatch hides unless active.
|
||
- History panel can be dismissed with the X button while leaving history lines visible (History tool brings it back).
|
||
- History records route modes from `ROUTE_HISTORY_ALLOWED_MODES` (default: `path`).
|
||
- Propagation render stays visible until a new render; origin changes only mark it dirty.
|
||
- Propagation now has an adjustable TX antenna gain (dBi) control, and Rx AGL defaults to 1m.
|
||
- Preview image endpoint renders in-bounds device dots for shared links.
|
||
- Peers tool opens a right-side panel showing incoming/outgoing neighbors (counts + %) based on rolling peer-history buckets; selecting a node draws peer lines on the map.
|
||
- Peer-history buckets are also updated from route `point_ids` when a hop cannot be drawn as a visible segment, so peer counts still reflect real adjacency for non-drawable hops.
|
||
- Peers tool ignores nodes listed in `MQTT_ONLINE_FORCE_NAMES` (used for observer listeners).
|
||
- Units toggle (km/mi) is stored in localStorage and defaults to `DISTANCE_UNITS`.
|
||
- PWA support is enabled via `/manifest.webmanifest` + `/sw.js` so mobile browsers can install the app.
|
||
- Clicking the logo toggles the left HUD panel while LOS/Propagation panels remain open.
|
||
- Node popups do not auto-pan; dragging the map won’t snap back to keep a popup in view.
|
||
- Node popups let users click the short key under the node name to copy the full public key, and can optionally show a MeshCore-compatible contact QR modal for that node with the node name plus a clickable truncated key that still copies the full public key.
|
||
- MQTT disconnect handler tolerates extra Paho args so the loop doesn’t crash; reconnects resume ingest.
|
||
- Share button copies a URL with `lat`, `lon`, `zoom`, `layer`, `history`, `heat`, `coverage`, `weather`, `labels`, `nodes`, `legend`, `menu`, `units`, and `history_filter` params.
|
||
- Weather state is not persisted in localStorage; it defaults off unless `weather=on` is in the URL.
|
||
- URL params override localStorage on load (`history=on` is the only way to load History open).
|
||
- Node size slider persists in localStorage (`meshmapNodeRadius`) and can be reset by clearing site data.
|
||
- MeshMapper coverage viewport sync reuses cached rectangles instead of rebuilding all visible squares on every pan/zoom, which keeps the Coverage layer responsive on busy maps.
|