diff --git a/CHANGELOG.md b/CHANGELOG.md index d8e3c2e..5cec403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,39 +1,40 @@ # CHANGELOG +## v0.2.0 + ## v0.1.0 -* Fix nested dataclass serialization for node snapshots -* Show daily node count in title and header by @l5yth in https://github.com/l5yth/potato-mesh/pull/49 -* Add daily date separators to chat log by @l5yth in https://github.com/l5yth/potato-mesh/pull/47 -* Feat: make frontend responsive for mobile by @l5yth in https://github.com/l5yth/potato-mesh/pull/46 -* Harden mesh utilities by @l5yth in https://github.com/l5yth/potato-mesh/pull/45 -* Filter out distant nodes from Berlin map view by @l5yth in https://github.com/l5yth/potato-mesh/pull/43 -* Display filtered active node counts in #MediumFast subheading by @l5yth in https://github.com/l5yth/potato-mesh/pull/44 -* Limit chat log and highlight short names by role by @l5yth in https://github.com/l5yth/potato-mesh/pull/42 -* Fix string/integer comparison in node query by @l5yth in https://github.com/l5yth/potato-mesh/pull/40 -* Escape chat message and node entries by @l5yth in https://github.com/l5yth/potato-mesh/pull/39 -* Sort chat entries by timestamp by @l5yth in https://github.com/l5yth/potato-mesh/pull/38 -* Feat: append messages to chat log by @l5yth in https://github.com/l5yth/potato-mesh/pull/36 -* Normalize future timestamps for nodes by @l5yth in https://github.com/l5yth/potato-mesh/pull/35 -* Optimize web frontend and Ruby app by @l5yth in https://github.com/l5yth/potato-mesh/pull/32 -* Add messages API endpoint with node details by @l5yth in https://github.com/l5yth/potato-mesh/pull/33 -* Clamp node timestamps and sync last_heard with position time by @l5yth in https://github.com/l5yth/potato-mesh/pull/31 -* Refactor: replace deprecated utcfromtimestamp by @l5yth in https://github.com/l5yth/potato-mesh/pull/30 -* Add optional debug logging for node and message operations by @l5yth in https://github.com/l5yth/potato-mesh/pull/29 -* Data: enable serial collection of messages on channel 0 by @l5yth in https://github.com/l5yth/potato-mesh/pull/25 -* Add first_heard timestamp by @l5yth in https://github.com/l5yth/potato-mesh/pull/23 -* Add persistent footer with contact information by @l5yth in https://github.com/l5yth/potato-mesh/pull/22 -* Sort initial chat entries by last-heard by @l5yth in https://github.com/l5yth/potato-mesh/pull/20 -* Display position time in relative 'time ago' format by @l5yth in https://github.com/l5yth/potato-mesh/pull/19 -* Adjust marker size and map tile opacity by @l5yth in https://github.com/l5yth/potato-mesh/pull/18 -* Add chat box for node notifications by @l5yth in https://github.com/l5yth/potato-mesh/pull/17 -* Color markers by role with grayscale map by @l5yth in https://github.com/l5yth/potato-mesh/pull/16 -* Default missing node role to client by @l5yth in https://github.com/l5yth/potato-mesh/pull/15 -* Show live node count in nodes page titles by @l5yth in https://github.com/l5yth/potato-mesh/pull/14 -* Filter stale nodes and add live search by @l5yth in https://github.com/l5yth/potato-mesh/pull/13 -* Remove raw node JSON column by @l5yth in https://github.com/l5yth/potato-mesh/pull/12 -* Add JSON ingest API for node updates by @l5yth in https://github.com/l5yth/potato-mesh/pull/11 -* Ignore Python __pycache__ directories by @l5yth in https://github.com/l5yth/potato-mesh/pull/10 -* Feat: load nodes from json for tests by @l5yth in https://github.com/l5yth/potato-mesh/pull/8 -* Handle dataclass fields in node snapshots by @l5yth in https://github.com/l5yth/potato-mesh/pull/6 -* Add index page and /nodes route for node map by @l5yth in https://github.com/l5yth/potato-mesh/pull/4 +* Show daily node count in title and header by @l5yth in +* Add daily date separators to chat log by @l5yth in +* Feat: make frontend responsive for mobile by @l5yth in +* Harden mesh utilities by @l5yth in +* Filter out distant nodes from Berlin map view by @l5yth in +* Display filtered active node counts in #MediumFast subheading by @l5yth in +* Limit chat log and highlight short names by role by @l5yth in +* Fix string/integer comparison in node query by @l5yth in +* Escape chat message and node entries by @l5yth in +* Sort chat entries by timestamp by @l5yth in +* Feat: append messages to chat log by @l5yth in +* Normalize future timestamps for nodes by @l5yth in +* Optimize web frontend and Ruby app by @l5yth in +* Add messages API endpoint with node details by @l5yth in +* Clamp node timestamps and sync last_heard with position time by @l5yth in +* Refactor: replace deprecated utcfromtimestamp by @l5yth in +* Add optional debug logging for node and message operations by @l5yth in +* Data: enable serial collection of messages on channel 0 by @l5yth in +* Add first_heard timestamp by @l5yth in +* Add persistent footer with contact information by @l5yth in +* Sort initial chat entries by last-heard by @l5yth in +* Display position time in relative 'time ago' format by @l5yth in +* Adjust marker size and map tile opacity by @l5yth in +* Add chat box for node notifications by @l5yth in +* Color markers by role with grayscale map by @l5yth in +* Default missing node role to client by @l5yth in +* Show live node count in nodes page titles by @l5yth in +* Filter stale nodes and add live search by @l5yth in +* Remove raw node JSON column by @l5yth in +* Add JSON ingest API for node updates by @l5yth in +* Ignore Python __pycache__ directories by @l5yth in +* Feat: load nodes from json for tests by @l5yth in +* Handle dataclass fields in node snapshots by @l5yth in +* Add index page and /nodes route for node map by @l5yth in diff --git a/LICENSE b/LICENSE index 261eeb9..289a47d 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2025 l5yth Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 20dc09e..9038d16 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,31 @@ -# potato-mesh +# Potato-Mesh -a simple meshtastic node dashboard for your local community. _no mqtt clutter, just local lora aether._ +A simple Meshtastic-powered node dashboard for your local community. _No MQTT clutter, just local LoRa aether._ -demo for berlin mediumfast: [potatomesh.net](https://potatomesh.net) +* Web app with chat window and map view showing nodes and messages. +* API to POST (authenticated) and to GET nodes and messages. +* Supplemental Python ingestor to feed the POST APIs of the Web app with data remotely. +* Shows new node notifications (first seen) in chat. +* Allows searching and filtering for nodes in map and table view. -![screenshot of the first version](./scrot-0.1.png) +Live demo for Berlin #MediumFast: [potatomesh.net](https://potatomesh.net) -## status +![screenshot of the second version](./scrot-0.2.png) -_in active development._ +## Web App -what works: - -* updating nodes from a locally connected meshtastic device (via serial) -* awaiting messages on default channel (0) from a local meshtastic device -* storing nodes and messages in a local database (sqlite3) -* displaying nodes ordered by last seen in a web app table view -* displaying nodes by geographic coordinates on a map layer, coloured by device role -* displaying new node notifications and chat messages in default channel in chat box -* displaying active node count and filtering nodes by name -* exposing nodes and messages to api endpoints -* posting nodes and messages to the api endpoints with authentication - -## requirements - -requires a meshtastic node connected (via serial) to gather mesh data and the meshtastic cli. - -requires the meshtastic python api for communicating with the device. +Requires Ruby for the Sinatra web app and SQLite3 for the app's database. ```bash -python -m venv .venv -source .venv/bin/activate -pip install -U meshtastic -``` - -requires latest ruby and ruby gems for the sinatra web app. - -```bash -gem install bundler +pacman -S ruby sqlite3 +gem install sinatra sqlite3 rackup puma rspec rack-test rufo +cd ./web bundle install ``` -### ingest +### Run -uses the meshtastic python library to ingest mesh data and post nodes and messages -to the configured potatomesh instance. - -run `mesh.sh` in `data/` with `POTATOMESH_INSTANCE` and `API_TOKEN` to keep updating -node records and parsing new incoming messages. - -```bash -POTATOMESH_INSTANCE=https://potatomesh.net API_TOKEN=TOKEN MESH_SERIAL=/dev/ttyACM0 DEBUG=1 ./mesh.sh -[...] -[debug] upserted node !849b7154 shortName='7154' -[debug] upserted node !ba653ae8 shortName='3ae8' -[debug] upserted node !16ced364 shortName='Pat' -[debug] stored message from '!9ee71c38' to '^all' ch=0 text='Guten Morgen!' -``` - -enable debug output with `DEBUG=1`, specify the serial port with `MESH_SERIAL` -(default `/dev/ttyACM0`). - -### web app - -uses a ruby sinatra webapp to display data from the sqlite database - -run `app.sh` in `web/` to run the sinatra webserver and check -[127.0.0.1:41447](http://127.0.0.1:41447/) for the correct node map. +Check out the `app.sh` run script in `./web` directory. ```bash API_TOKEN="1eb140fd-cab4-40be-b862-41c607762246" ./app.sh @@ -78,33 +37,73 @@ Puma starting in single mode... * Listening on http://127.0.0.1:41447 ``` -set `API_TOKEN` required for authorizations on the api post-endpoints. +Check [127.0.0.1:41447](http://127.0.0.1:41447/) for the development preview +of the node map. Set `API_TOKEN` required for authorizations on the API's POST endpoints. -the web app can be configured with environment variables (defaults shown): +The web app can be configured with environment variables (defaults shown): * `SITE_NAME` - title and header shown in the ui (default: "Meshtastic Berlin") * `DEFAULT_CHANNEL` - default channel shown in the ui (default: "#MediumFast") +* `DEFAULT_FREQUENCY` - default channel shown in the ui (default: "868MHz") * `MAP_CENTER_LAT` / `MAP_CENTER_LON` - default map center coordinates (default: `52.502889` / `13.404194`) * `MAX_NODE_DISTANCE_KM` - hide nodes farther than this distance from the center (default: `137`) * `MATRIX_ROOM` - matrix room id for a footer link (default: `#meshtastic-berlin:matrix.org`) -example: +Example: ```bash SITE_NAME="Meshtastic Berlin" MAP_CENTER_LAT=52.502889 MAP_CENTER_LON=13.404194 MAX_NODE_DISTANCE_KM=137 MATRIX_ROOM="#meshtastic-berlin:matrix.org" ./app.sh ``` -## api +### API -the web app contains an api: +The web app contains an API: -* GET `/api/nodes?limit=1000` - returns the latest 1000 nodes reported to the app -* GET `/api/messages?limit=1000` - returns the latest 1000 messages +* GET `/api/nodes?limit=100` - returns the latest 100 nodes reported to the app +* GET `/api/messages?limit=100` - returns the latest 100 messages * POST `/api/nodes` - upserts nodes provided as JSON object mapping node ids to node data (requires `Authorization: Bearer `) * POST `/api/messages` - appends messages provided as a JSON object or array (requires `Authorization: Bearer `) -the `API_TOKEN` environment variable must be set to a non-empty value and match the token supplied in the `Authorization` header for `POST` requests. +The `API_TOKEN` environment variable must be set to a non-empty value and match the token supplied in the `Authorization` header for `POST` requests. -## license +## Python Ingestor -apache v2.0 +The web app is not meant to be run locally connected to a Meshtastic node but rather +on a remote host without access to a physical Meshtastic device. Therefore, it only +accepts data through the API POST endpoints. Benefit is, here multiple nodes across the +community can feed the dashboard with data. The web app handles messages and nodes +by ID and there will be no duplication. + +For convenience, the directory `./data` contains a Python ingestor. It connects to a local +Meshtastic node via serial port to gather nodes and messages seen by the node. + +```bash +pacman -S python +cd ./data +python -m venv .venv +source .venv/bin/activate +pip install -U meshtastic +``` + +It uses the Meshtastic Python library to ingest mesh data and post nodes and messages +to the configured potato-mesh instance. + +Check out `mesh.sh` ingestor script in the `./data` directory. + +```bash +POTATOMESH_INSTANCE=http://127.0.0.1:41447 API_TOKEN=1eb140fd-cab4-40be-b862-41c607762246 MESH_SERIAL=/dev/ttyACM0 DEBUG=1 ./mesh.sh +Mesh daemon: nodes+messages → http://127.0.0.1 | port=41447 | channel=0 +[...] +[debug] upserted node !849b7154 shortName='7154' +[debug] upserted node !ba653ae8 shortName='3ae8' +[debug] upserted node !16ced364 shortName='Pat' +[debug] stored message from '!9ee71c38' to '^all' ch=0 text='Guten Morgen!' +``` + +Run the script with `POTATOMESH_INSTANCE` and `API_TOKEN` to keep updating +node records and parsing new incoming messages. Enable debug output with `DEBUG=1`, +specify the serial port with `MESH_SERIAL` (default `/dev/ttyACM0`), etc. + +## License + +Apache v2.0, Contact diff --git a/scrot-0.2.png b/scrot-0.2.png new file mode 100644 index 0000000..4063c49 Binary files /dev/null and b/scrot-0.2.png differ diff --git a/web/Gemfile b/web/Gemfile index 262a55c..5d407b9 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -8,4 +8,5 @@ gem "puma", "~> 7.0" group :test do gem "rspec", "~> 3.12" gem "rack-test", "~> 2.1" + gem "rufo", "~> 0.18.1" end