Operate beyond the grid.
SolarSentinel is an open-source field-network project for local coordination, outdoor training, exploration, and scenario-based exercises. The working application is a browser dashboard for node check-ins and field reports, backed by a local Flask server and SQLite database.
The broader vision combines a solar-powered field server, ESP32/LoRa nodes, radio gateways, offline reference material, and local AI. Those integrations are at different stages: this repository includes working dashboard code, starter firmware, sample data, and design documents. See project status before planning a deployment.
- Project status
- What the dashboard does
- Quickstart
- Using the dashboard
- Architecture
- Configuration and deployment
- API examples
- Data, backups, and recovery
- Hardware and integration guides
- Repository layout
- Development and validation
- Troubleshooting
- Roadmap and contributions
- License and links
Early working implementation. The dashboard can be run and tested independently of field hardware. Field reliability and the complete off-grid system have not been validated end to end.
| Area | Available in this repository | Current boundary |
|---|---|---|
| Field dashboard | Responsive interface, node check-ins, reports, SQLite persistence, operator-token writes | Working local application |
| Node telemetry | Validated HTTP check-in API, timestamps, freshness and battery indicators | Measurements must be supplied manually or by an adapter |
| Solar monitoring | Example monitoring script and hardware notes | The existing script generates random simulated readings |
| ESP32 / LoRa | Starter Arduino sketches and network plans | No dashboard gateway or verified mesh implementation |
| APRS / packet radio | Setup and integration guides | No connected dashboard adapter |
| Field-node PWA | Specification, deployment notes, and install-page placeholder | No installable app, service worker, or background synchronization |
| Offline knowledge | Kiwix setup guide and Compose example | Separate service; content must be supplied and configured |
| Local AI / RAG | Ollama setup notes and integration plans | No working dashboard AI or retrieval pipeline |
| Missions / RPG | Sample scenario JSON, node profiles, and engine plans | No executable mission engine, XP system, or campaign runner |
| AR / VR | Expansion ideas and AR planning notes | Future work |
The project aims to support training and field coordination. The current implementation is a prototype rather than a validated emergency-response system.
- Shows registered nodes, recent check-ins, nodes needing attention, and the total number of open reports.
- Stores the latest node snapshot, including optional battery, voltage, and coordinates.
- Separates online, stale, and offline nodes using server receipt time.
- Creates field reports with routine, important, or urgent priority; reports can be resolved and reopened.
- Filters the latest 200 reports by open, urgent, or resolved status.
- Saves one report draft in the browser and prevents duplicate submissions when an identical request is retried.
- Uses a shared operator token for updates and keeps the token in page memory.
- Runs with locally served assets and an explicitly labeled demo workspace.
An internet connection is needed to obtain the code and dependencies during setup. Once installed, the dashboard does not need external scripts, fonts, or map services. Browsers still need access to the running field server to load the application and submit updates.
- Python 3.10 or newer with
pipandvenvsupport. - Git to clone the repository, or an extracted source archive.
- A modern browser.
- Node.js only if you want to run the JavaScript syntax check.
The dashboard does not require a Raspberry Pi, radio, GPS receiver, solar equipment, Ollama, Docker, or Apache to try it locally. The commands below assume you are starting from a terminal and run subsequent commands in the repository root.
git clone https://github.com/CptNope/Solar-Sentinel.git
cd Solar-Sentinel
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe -m dashboard.app --demogit clone https://github.com/CptNope/Solar-Sentinel.git
cd Solar-Sentinel
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python -m dashboard.app --demoOpen http://127.0.0.1:5000. Keep the terminal running; press Ctrl+C there to stop the server. These commands invoke the virtual environment directly, so shell activation is optional.
On first startup, the server generates an access token beside its database. For the demo workspace, read it locally:
# Windows PowerShell
Get-Content dashboard/instance/demo.token# Linux / Raspberry Pi
cat dashboard/instance/demo.tokenChoose Operator access in the dashboard, paste the token, and select Use token. Reloading the page clears browser access; re-enter the token when needed. Clear access locks updates for that page.
Stop the demo server and run the same command without --demo:
# Windows PowerShell
.\.venv\Scripts\python.exe -m dashboard.app# Linux / Raspberry Pi
.venv/bin/python -m dashboard.appThe live workspace starts empty and uses a separate database and token. "Live" selects the normal workspace; it does not connect hardware automatically.
| Workspace | Default database | Generated token file |
|---|---|---|
| Live | dashboard/instance/sentinel.sqlite3 |
dashboard/instance/sentinel.token |
| Demo | dashboard/instance/demo.sqlite3 |
dashboard/instance/demo.token |
Demo data is seeded once into its database. Its timestamps age naturally, so sample nodes eventually become stale or offline. Restarting preserves existing data rather than resetting the samples.
- Open the dashboard and enable operator access.
- Choose Check in a node. Give it a stable ID and display name. Battery, voltage, and coordinates are optional.
- Submit another check-in with the same ID to replace that node's latest snapshot.
- Choose New field report, enter a call sign or team, select a priority, and describe the update.
- Use the report filter and Mark resolved / Reopen report to manage activity.
| Indicator | Meaning |
|---|---|
| Online | Last check-in received less than 120 seconds ago |
| Stale | Last check-in received from 120 to under 600 seconds ago |
| Offline | No check-in received for at least 600 seconds |
| Needs attention | A node is overdue or has a fresh battery reading at or below 20%; each node is counted once |
Dash (—) |
The node did not supply that measurement |
| Last reported | A measurement belongs to a stale or offline snapshot |
These connection states describe check-in freshness, not a verified radio link. The server does not estimate battery percentage from voltage or distinguish panel voltage from battery voltage. An adapter must supply measurements with the intended meaning.
The page refreshes every 10 seconds while visible. If the browser loses contact with the server, it displays a warning and retains the last received data. That data may be outdated.
One report draft is saved in that browser profile's localStorage. Reconnect and submit it manually; successful submission clears the draft. There is no background queue or automatic offline synchronization. Drafts are local to the device, while submitted reports live on the server. Clear browser site data to remove a saved draft.
The implemented dashboard path is:
Browser dashboard Manual/API clients
HTML + CSS + JavaScript Node measurements / reports
| |
+-------------- HTTP --------------+
|
Waitress server
|
Flask application
Validation + write token
|
SQLite database
Latest nodes + field reports
The interface and API are served from the same application. / and /status open the dashboard. Node snapshots are replaced on check-in; the application does not retain a telemetry history. Reports remain stored even when resolved, while the dashboard lists only the latest 200.
Planned integrations would feed this application through adapters: physical sensors or LoRa/APRS gateways for telemetry, a field-node PWA for offline interaction, and optional Kiwix or Ollama services for reference material and AI assistance. These services are not started by the dashboard command.
The CLI starts the application through Waitress. Run commands from the repository root.
| Setting | Default | Purpose |
|---|---|---|
--host |
127.0.0.1 |
Interface to listen on |
--port |
5000 |
HTTP port |
--demo |
Off | Select the separate demo database and seed sample data if empty |
SENTINEL_DB |
Live database path shown above | Override the live SQLite path; ignored by --demo |
SENTINEL_TOKEN |
Generated token file | Supply the shared write credential instead of loading/generating a token file |
Relative SENTINEL_DB paths are resolved from the process's working directory. Use an absolute path for a service deployment.
To listen on a trusted local network, add --host 0.0.0.0:
.venv/bin/python -m dashboard.app --host 0.0.0.0 --port 5000On Windows, use .\.venv\Scripts\python.exe in place of .venv/bin/python. From another device on the same network, open http://<field-server-IP>:5000. 0.0.0.0 is a bind address, not a browser destination. Hostname discovery, a Wi-Fi access point, and firewall rules are not configured by this application.
Anyone who can reach the server can read node positions and reports. Updates require a shared bearer token. The application does not provide individual accounts, roles, token expiry, or a per-operator audit trail.
For shared or untrusted networks, use an HTTPS reverse proxy with appropriate authentication and restrict direct access to the application port. Plain HTTP exposes tokens and field data in transit. Protect the database and token files with host permissions; they are excluded from Git.
The existing software/pi-setup.sh is a starter for broader system setup. It does not replace the dashboard quickstart or install a complete integrated field network.
Read endpoints require no token. All writes require Authorization: Bearer YOUR_TOKEN and JSON content. The request-body limit is 16 KB.
| Method and route | Behavior |
|---|---|
GET /api/health |
Check database connectivity and return the demo flag |
GET /api/state |
Return nodes, latest 200 reports, total open report count, demo flag, and server timestamp |
POST /api/access |
Validate an operator token |
POST /api/nodes |
Create or replace a node snapshot |
POST /api/reports |
Create a report, or return the existing report for an identical retry |
PATCH /api/reports/<id> |
Resolve or reopen a report |
The following examples use a POSIX shell with curl. Set a token for the workspace you started; the examples use the default live workspace:
SENTINEL_API_TOKEN="$(cat dashboard/instance/sentinel.token)"
curl --fail-with-body http://127.0.0.1:5000/api/healthIf you supplied SENTINEL_TOKEN when starting the server, use that credential instead of reading a generated file. For a demo server, use dashboard/instance/demo.token.
curl --fail-with-body http://127.0.0.1:5000/api/nodes \
-H "Authorization: Bearer $SENTINEL_API_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"id":"trail-01","name":"Trail team","battery":72,"voltage":12.8,"latitude":35.61,"longitude":-82.55}'id and name are required. Optional numbers may be omitted or set to null. Each check-in replaces the snapshot: omitting a previous measurement clears it to unknown. Supply latitude and longitude together, or neither. Suggested adapter check-in interval: 60 seconds.
| Node field | Accepted value |
|---|---|
id |
Nonblank string, up to 64 characters |
name |
Nonblank string, up to 80 characters |
battery |
Number from 0 to 100, or null |
voltage |
Number from 0 to 100, or null |
latitude |
Number from -90 to 90, or null |
longitude |
Number from -180 to 180, or null |
curl --fail-with-body http://127.0.0.1:5000/api/reports \
-H "Authorization: Bearer $SENTINEL_API_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"request_id":"trail-01-report-001","author":"Trail team","priority":"important","message":"At the north junction. Next check-in in five minutes."}'author is required and limited to 80 characters; message is required and limited to 2,000 characters. priority is routine (the default), important, or urgent. request_id is a required nonblank string of up to 64 characters.
Generate a new request ID for each new report. Reuse that ID only to retry the same author, message, and priority after an uncertain response. A new report returns HTTP 201; an identical retry returns the existing ID with HTTP 200. Changed content using an existing request ID returns HTTP 409.
Use the returned report ID in the update route; this example assumes the returned ID was 1:
curl --fail-with-body -X PATCH http://127.0.0.1:5000/api/reports/1 \
-H "Authorization: Bearer $SENTINEL_API_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"resolved":true}'Set resolved to false to reopen it. Common errors are 400 for invalid input, 401 for an invalid/missing token, 404 for an unknown report ID, and 413 for an oversized request. See the dashboard reference for further behavior notes.
SQLite data survives application restarts. The application stores the latest snapshot per node and all submitted reports, including resolved reports. It does not automatically expire records or expose a delete operation.
To back up a workspace:
- Stop its server so no writes are in progress.
- Copy the database and any adjacent SQLite
-wal/-shmfiles as one stopped-workspace snapshot, or copy the whole instance directory. - Keep the token file with the backup if you want to retain its generated credential. If you use
SENTINEL_TOKEN, manage that secret separately. - Restore files while the server is stopped, preserving permissions and the configured database path, then restart.
To reseed a disposable demo, stop the server and move its database and adjacent SQLite files to a backup location before restarting with --demo. Keep the live workspace untouched. Removing only the demo token file generates a new credential on the next start unless SENTINEL_TOKEN is set.
Browser drafts are separate from server backups. Clearing site data removes those drafts and does not delete submitted reports.
Start with the software dashboard before adding hardware. Existing hardware lists and sketches are starting points; board pinouts, radio drivers, power requirements, and gateway behavior need verification for your chosen components.
| Topic | Starting point |
|---|---|
| Field-server equipment | Parts list and solar setup notes |
| ESP32 firmware | Firmware examples |
| LoRa topology | Network plan |
| Android APRS | APRSdroid setup |
| Radio integration | APRS/VHF setup and BBS over radio |
| Field-node app | PWA specification |
| Offline reference library | Kiwix setup and Compose example |
| Local AI | Ollama setup notes and RAG plan |
| Scenarios and profiles | Mission samples and node profile templates |
dashboard/ Working Flask app, browser assets, and dashboard reference
tests/ Backend behavior tests
.github/workflows/ Automated dashboard checks
requirements.txt Dashboard Python dependencies
firmware/ Starter ESP32 / LoRa / GPS sketches
hardware/ Equipment lists and solar setup notes
software/ Pi setup starter, simulated solar monitor, BBS plans
lora/ LoRa network and AI integration plans
radio/ APRS, radio-cabling, and BBS guides
android/ Android/APRS setup notes
pwa/ Field-node specifications and placeholder install page
ai/ Local-model, categorization, RAG, and mission-engine plans
wiki/ Kiwix setup and Compose example
scenarios/ Example mission JSON
node-profiles/ Example node-profile JSON
assets/ Project icon
docs/ Project-site content, launch material, and diagram files
After installing the requirements, run the backend suite from the repository root:
# Windows PowerShell
.\.venv\Scripts\python.exe -m unittest discover -s tests -v# Linux / Raspberry Pi
.venv/bin/python -m unittest discover -s tests -vWith Node.js installed:
node --check dashboard/static/dashboard.js
git diff --checkGitHub Actions runs the backend tests and JavaScript syntax check on pushes and pull requests using Python 3.12 and Node.js 22. The tests cover unknown telemetry, authenticated writes, node updates, freshness boundaries, invalid input, report retries, persistence, demo isolation, and response headers.
For interface changes, also verify desktop and phone layouts, keyboard access, report draft recovery after reload, and server-disconnection behavior. Automated checks do not validate physical radios, solar autonomy, or complete field operation.
| Symptom | What to check |
|---|---|
ModuleNotFoundError: flask or waitress |
Install requirements.txt with the same virtual-environment Python used to launch the app. |
| No nodes or reports appear | A live workspace starts empty. Check in a node, submit a report, or restart with --demo for samples. |
| Demo nodes become stale/offline | Sample timestamps age naturally. This is expected; demo mode does not emit ongoing check-ins. |
| Updates return 401 | Re-enter the token for the active workspace. An environment-supplied token overrides generated token-file loading. |
| Server unavailable warning | Confirm the server process is running, the URL/port is correct, and the device can reach it. Last displayed readings may be old. |
| Another device cannot connect | Bind to 0.0.0.0, use the server's actual LAN IP, and check network isolation and host firewall settings. |
| Port is already in use | Stop the other process or start with another port, such as --port 5001. |
| A previous measurement disappeared | Check-ins replace the full snapshot. Resend every measurement you want to retain. |
| Open count exceeds the visible reports | The open count includes all persisted reports; the activity list and filters cover only the latest 200. |
| A draft is missing on another phone | Drafts stay in the browser profile where they were composed. They are not server-synchronized. |
solarsentinel.local does not resolve |
Automatic hostname discovery is not configured here. Use the field server's IP address. |
The next useful milestones are:
- Measured telemetry: connect a real sensor adapter, document units and provenance, and validate it on field hardware.
- Gateway ingestion: bridge LoRa/APRS data into authenticated check-ins with explicit delivery and identity behavior.
- Offline field client: implement an installable app, local storage, and a reliable synchronization protocol.
- Operational controls: add per-operator permissions, audit history, and documented backup/upgrade procedures.
- Mission tooling: validate scenario schemas and build an executable mission workflow.
- Optional knowledge and AI: integrate reference search and local assistance after the core data flow is reliable.
AR/VR overlays and RPG expansion remain longer-term ideas. This is a direction for contributions, not a release schedule.
To contribute, open an issue describing the problem or proposed integration, then submit a focused pull request. Include setup instructions, relevant tests, and the hardware/OS used for integration testing. Keep real field locations, credentials, databases, and personal reports out of commits. New scenario packs and node profiles should identify required capabilities and distinguish examples from tested configurations.
SolarSentinel is released under the MIT License.
- Source repository and issues
- Project site
- Dashboard reference
- Project founder: Jeremy Anderson · Website
- Built by Jeremy Anderson Tech · Project page