Initial commit

This commit is contained in:
MarekWo
2025-12-21 12:31:28 +01:00
commit 1b3c583e04
4 changed files with 1196 additions and 0 deletions

214
CLAUDE_CODE_PROMPT.md Normal file
View File

@@ -0,0 +1,214 @@
# mc-webui: Initial Prompt for Claude Code
## Project Context
You are continuing development of **mc-webui** - a lightweight web interface for meshcore-cli. This project was designed collaboratively with Claude (Opus) and documented in PRD.md.
## Project Summary
**What:** A Flask-based web UI that wraps meshcore-cli, providing browser access to MeshCore mesh network.
**Why:** To eliminate the need for SSH/terminal access when using MeshCore chat on a Heltec V4 device connected to a Debian VM.
**Target:** Single-user, trusted local network, Docker deployment.
## Technical Stack
- **Backend:** Python 3.11+, Flask
- **Frontend:** HTML5, Bootstrap 5, vanilla JavaScript
- **Deployment:** Docker / Docker Compose
- **Communication:** subprocess calls to `meshcli`
- **Data source:** `~/.config/meshcore/<device_name>.msgs` (JSON Lines)
## MVP Features
1. **View messages** - display chat history from Public channel (auto-refresh every 60s)
2. **Send messages** - publish to Public channel
3. **Reply to users** - `@[UserName] content` format
4. **Clean contacts** - remove inactive contacts (configurable hours threshold)
## Key Files to Create
```
mc-webui/
├── docker/
│ ├── Dockerfile
│ └── docker-compose.yml
├── app/
│ ├── __init__.py
│ ├── main.py # Flask entry point
│ ├── config.py # Configuration from env vars
│ ├── meshcore/
│ │ ├── __init__.py
│ │ ├── cli.py # meshcli wrapper (subprocess)
│ │ └── parser.py # .msgs file parser
│ ├── routes/
│ │ ├── __init__.py
│ │ ├── api.py # REST API endpoints
│ │ └── views.py # HTML views
│ ├── static/
│ │ ├── css/
│ │ │ └── style.css
│ │ └── js/
│ │ └── app.js
│ └── templates/
│ ├── base.html
│ ├── index.html
│ └── components/
│ ├── message.html
│ └── navbar.html
├── requirements.txt
├── .env.example
├── .gitignore
├── README.md
└── PRD.md # Already created - see attached
```
## Environment Variables
```env
MC_SERIAL_PORT=/dev/serial/by-id/usb-Espressif_Systems_heltec_wifi_lora_32_v4__16_MB_FLASH__2_MB_PSRAM__90706984A000-if00
MC_DEVICE_NAME=MarWoj
MC_CONFIG_DIR=/root/.config/meshcore
MC_REFRESH_INTERVAL=60
MC_INACTIVE_HOURS=48
FLASK_HOST=0.0.0.0
FLASK_PORT=5000
FLASK_DEBUG=false
```
## meshcli Commands Reference
```bash
# Base command (alias 'mc' exists on host)
meshcli -s <SERIAL_PORT> <command>
# Sync/fetch messages
meshcli -s <PORT> recv
# Send to Public channel
meshcli -s <PORT> public "message text"
# Reply to user (still goes to Public)
meshcli -s <PORT> public "@[UserName] message text"
# Get contact list
meshcli -s <PORT> contacts
# Clean inactive contacts (type=1 is client, u<48h = updated less than 48h ago)
meshcli -s <PORT> "apply_to u<48h,t=1 remove_contact"
# Device info
meshcli -s <PORT> infos
```
## Message Format (.msgs file - JSON Lines)
```json
// Received message
{"type": "CHAN", "SNR": 10.5, "channel_idx": 0, "path_len": 5, "txt_type": 0, "sender_timestamp": 1766300840, "text": "UserName🤖: Hello everyone", "name": "channel 0", "timestamp": 1766300846}
// Sent message
{"type": "SENT_CHAN", "channel_idx": 0, "text": "My message", "txt_type": 0, "name": "MarWoj", "timestamp": 1766309432}
```
**Key fields:**
- `type`: "CHAN" (received) or "SENT_CHAN" (sent)
- `text`: For received - includes "SenderName: message", for sent - just the message
- `timestamp`: Unix timestamp
- `channel_idx`: 0 = Public channel
- `name`: For sent messages - sender's device name
## API Endpoints (to implement)
```
GET /api/messages # List messages (optional: ?limit=100&offset=0)
POST /api/messages # Send message {"text": "...", "reply_to": "UserName" (optional)}
GET /api/status # Device/connection status
POST /api/contacts/cleanup # Clean inactive contacts {"hours": 48}
GET /api/device/info # Device information
```
## Implementation Order (Phases)
### Phase 0: Environment Setup
- Create directory structure
- Dockerfile (Python 3.11, install meshcore-cli via pip)
- docker-compose.yml (mount serial device, mount .config/meshcore)
- .env.example, .gitignore
- Basic README.md
### Phase 1: Backend Basics
- Flask app skeleton
- config.py (load from environment)
- meshcore/cli.py (subprocess wrapper with timeout handling)
- meshcore/parser.py (read and parse .msgs file)
- Basic API endpoints
### Phase 2: Frontend Chat View
- base.html (Bootstrap 5 CDN)
- index.html (chat layout)
- Message list with own/others distinction
- Time formatting (relative or absolute)
### Phase 3: Message Sending
- Send form with validation
- Toast notifications for feedback
- Reply button functionality
### Phase 4: Auto-refresh
- JavaScript polling every 60s
- Last refresh indicator
- Manual refresh button
- Smart scroll (don't interrupt user scrolling)
### Phase 5: Contact Management
- Settings modal
- Cleanup function with confirmation
- Result feedback
### Phase 6: Polish
- Error handling
- Loading states
- README with full documentation
- Docker image optimization
## Important Notes
1. **All code, comments, and documentation must be in English** (open source project)
2. **Keep it simple** - this is MVP, avoid over-engineering
3. **Error handling** - meshcli can timeout or fail, handle gracefully
4. **No authentication** - trusted network assumption
5. **Test with real device** - the Heltec V4 is connected via USB to this VM
## Current State
- PRD.md is complete and attached/available
- No code has been written yet
- Development environment: VS Code Remote SSH to Debian VM
- Docker not yet installed on VM (will install when needed)
## First Task
Start with **Phase 0: Environment Setup**:
1. Create the directory structure
2. Create Dockerfile
3. Create docker-compose.yml
4. Create .env.example
5. Create .gitignore
6. Create initial README.md
Then proceed to Phase 1 when ready.
---
## Reference: Full PRD
The complete PRD.md document is available in this repository. It contains:
- Detailed requirements (functional and non-functional)
- UI wireframes
- Architecture diagrams
- Risk analysis
- Future roadmap
Please refer to PRD.md for any detailed specifications.

545
PRD.md Normal file
View File

@@ -0,0 +1,545 @@
# PRD: mc-webui
## Product Requirements Document
**Version:** 1.0
**Date:** 2025-01-21
**Author:** Marek / Claude AI
---
## 1. Project Overview
### 1.1 Project Name
**mc-webui** - A simple web interface for meshcore-cli
### 1.2 Description
mc-webui is a lightweight web application that provides convenient access to the MeshCore network without requiring terminal/SSH access. The application serves as a wrapper around meshcore-cli, offering a user-friendly interface for viewing messages, sending communications, and basic contact management.
### 1.3 Problem Statement
Currently, using the MeshCore network via a Heltec V4 device connected to a Debian server requires:
- SSH connection to the virtual machine
- Knowledge of meshcore-cli commands
- Manual command entry in the terminal
mc-webui eliminates these barriers by providing browser-based access from any device on the local network.
### 1.4 Target Users
- Single user (single-user deployment)
- Access from multiple devices (computer, tablet, phone) via web browser
- Environment: trusted local network (no authentication requirement)
### 1.5 Existing Solutions
| Project | Assessment | Why It Doesn't Fit |
|---------|------------|-------------------|
| meshcore-hub | Feature-rich | Requires MQTT, too complex for single-user |
| MeshCore App (Flutter) | Official app | Requires BLE/WiFi, doesn't work with Serial over network |
| MeshTUI | Terminal UI | Still requires SSH |
mc-webui fills the niche of a **simple, lightweight web solution** for a single device.
---
## 2. Goals and Assumptions
### 2.1 Primary Goals (MVP)
1. **View messages** - display chat history from the Public channel
2. **Send messages** - publish to the Public channel
3. **Reply to users** - using `@[UserName] content` format
4. **Manage contacts** - clean up inactive contacts
### 2.2 Technical Assumptions
- **Backend:** Python 3.11+ with Flask
- **Frontend:** HTML5, Bootstrap 5, vanilla JavaScript
- **Deployment:** Docker / Docker Compose
- **MeshCore communication:** subprocess calls to `meshcli`
- **Data source:** `~/.config/meshcore/<device_name>.msgs` file
- **Refresh rate:** automatic every 60 seconds + manual button
### 2.3 Design Assumptions
- Minimalism - only essential features at launch
- Simple configuration - environment variables
- Responsive design - works on phone and desktop
- No authentication - trusted local network
---
## 3. Technical Architecture
### 3.1 Architecture Diagram
```
┌─────────────────────────────────────────────────────────────────┐
│ Host (Debian VM) │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Docker Container │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Flask │◄────►│ meshcli │ │ │
│ │ │ Backend │ │ (subprocess) │ │
│ │ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Bootstrap │ │ .msgs file │ │ │
│ │ │ Frontend │ │ (mounted) │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Heltec V4 │ │
│ │ (USB Serial) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ HTTP :5000
┌─────────────────┐
│ Browser │
│ (LAN/VPN) │
└─────────────────┘
```
### 3.2 Components
#### 3.2.1 Backend (Flask)
- **HTTP server** on port 5000
- **REST API** for frontend communication
- **meshcli wrapper** - command execution via subprocess
- **Parser for .msgs** - reading and parsing JSON Lines file
#### 3.2.2 Frontend (Bootstrap 5)
- **Chat view** - message list in messenger style
- **Send form** - text input field + button
- **Management panel** - contact cleanup
- **Auto-refresh** - JavaScript polling every 60s
#### 3.2.3 meshcore-cli Integration
Commands executed via subprocess:
```bash
# Fetch messages (trigger sync)
meshcli -s <SERIAL_PORT> recv
# Send to Public channel
meshcli -s <SERIAL_PORT> public "message content"
# Reply to user
meshcli -s <SERIAL_PORT> public "@[UserName] content"
# Clean up inactive contacts
meshcli -s <SERIAL_PORT> "apply_to u<48h,t=1 remove_contact"
# Get contact list
meshcli -s <SERIAL_PORT> contacts
```
### 3.3 Project File Structure
```
mc-webui/
├── docker/
│ ├── Dockerfile
│ └── docker-compose.yml
├── app/
│ ├── __init__.py
│ ├── main.py # Flask entry point
│ ├── config.py # Configuration
│ ├── meshcore/
│ │ ├── __init__.py
│ │ ├── cli.py # meshcli wrapper (subprocess)
│ │ └── parser.py # .msgs file parser
│ ├── routes/
│ │ ├── __init__.py
│ │ ├── api.py # API endpoints
│ │ └── views.py # HTML views
│ ├── static/
│ │ ├── css/
│ │ │ └── style.css # Custom styles
│ │ └── js/
│ │ └── app.js # Frontend logic
│ └── templates/
│ ├── base.html # Base template
│ ├── index.html # Main chat view
│ └── components/
│ ├── message.html # Message component
│ └── navbar.html # Navigation
├── requirements.txt
├── .env.example
├── README.md
└── PRD.md
```
---
## 4. Functional Requirements (MVP)
### 4.1 Module: Message Viewing
| ID | Requirement | Priority |
|----|-------------|----------|
| F1.1 | System displays message list from .msgs file | MUST |
| F1.2 | Messages are sorted chronologically (newest at bottom) | MUST |
| F1.3 | Each message contains: sender, content, timestamp | MUST |
| F1.4 | Own messages are visually distinguished (e.g., different side) | SHOULD |
| F1.5 | Auto-scroll to newest message | SHOULD |
| F1.6 | Display emoji (Unicode) | SHOULD |
**Message format in .msgs (JSON Lines):**
```json
{"type": "CHAN", "SNR": 10.5, "channel_idx": 0, "text": "UserName: content", "timestamp": 1766300846}
{"type": "SENT_CHAN", "channel_idx": 0, "text": "content", "name": "MarWoj", "timestamp": 1766309413}
```
### 4.2 Module: Message Sending
| ID | Requirement | Priority |
|----|-------------|----------|
| F2.1 | Form with text field and "Send" button | MUST |
| F2.2 | Send via Enter (Shift+Enter = new line) | SHOULD |
| F2.3 | Validation - non-empty field | MUST |
| F2.4 | Status feedback (success/error) | MUST |
| F2.5 | Clear field after sending | MUST |
### 4.3 Module: Replying to Users
| ID | Requirement | Priority |
|----|-------------|----------|
| F3.1 | "Reply" button on each message | MUST |
| F3.2 | Click inserts `@[UserName] ` into text field | MUST |
| F3.3 | Focus moves to text field | SHOULD |
### 4.4 Module: Contact Management
| ID | Requirement | Priority |
|----|-------------|----------|
| F4.1 | "Clean inactive contacts" button | MUST |
| F4.2 | Configurable inactivity hours (default 48h) | SHOULD |
| F4.3 | Confirmation before execution (modal) | MUST |
| F4.4 | Feedback on number of removed contacts | SHOULD |
### 4.5 Module: Data Refresh
| ID | Requirement | Priority |
|----|-------------|----------|
| F5.1 | Automatic refresh every 60 seconds | MUST |
| F5.2 | Manual refresh button | MUST |
| F5.3 | Last refresh indicator | SHOULD |
| F5.4 | Loading indicator during refresh | SHOULD |
---
## 5. Non-Functional Requirements
### 5.1 Performance
| ID | Requirement |
|----|-------------|
| NF1.1 | API response time < 2 seconds |
| NF1.2 | Handle .msgs file up to 10,000 messages |
| NF1.3 | Minimal resource usage (< 100MB RAM) |
### 5.2 Usability
| ID | Requirement |
|----|-------------|
| NF2.1 | Responsive design (mobile-first) |
| NF2.2 | Readable on screens 320px - 2560px |
| NF2.3 | Basic view works without JavaScript (graceful degradation) |
### 5.3 Reliability
| ID | Requirement |
|----|-------------|
| NF3.1 | Handle meshcli errors (timeout, device unavailable) |
| NF3.2 | Log errors to stdout/stderr (Docker logs) |
| NF3.3 | Automatic container restart on failure |
### 5.4 Security
| ID | Requirement |
|----|-------------|
| NF4.1 | No authentication (trusted network) - deliberate decision |
| NF4.2 | Input sanitization (injection protection) |
| NF4.3 | No sensitive data storage |
---
## 6. User Interface
### 6.1 Wireframe - Main View
```
┌────────────────────────────────────────────────────────────┐
│ mc-webui [↻ Refresh] [⚙️] │
├────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 12:30 MarioTJE🤖 │ │
│ │ Hey everyone [↩️] │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 12:35 BBKr │ │
│ │ @[Mruk-A] no it's ID conflict 01 [↩️] │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────┐ │
│ │ 12:40 MarWoj (You) │ │
│ │ Good morning everyone │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 12:45 Zen │ │
│ │ Good morning everyone, greetings [↩️] │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
├────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────┐ ┌────────┐ │
│ │ Type a message... │ │ Send │ │
│ └──────────────────────────────────────────┘ └────────┘ │
├────────────────────────────────────────────────────────────┤
│ Last refresh: 12:46:30 Next in: 45s │
└────────────────────────────────────────────────────────────┘
```
### 6.2 Wireframe - Settings Panel (modal/dropdown)
```
┌─────────────────────────────────────┐
│ ⚙️ Settings ✕ │
├─────────────────────────────────────┤
│ │
│ Contact Management │
│ ───────────────────────────────── │
│ Remove contacts inactive for: │
│ [ 48 ] hours │
│ │
│ [🗑️ Clean Inactive Contacts] │
│ │
│ ───────────────────────────────── │
│ Device Information │
│ Name: MarWoj │
│ Port: /dev/serial/by-id/usb-... │
│ │
└─────────────────────────────────────┘
```
### 6.3 Colors and Style
- **Theme:** Light (dark mode possibility in future)
- **Primary colors:**
- Primary: #0d6efd (Bootstrap blue)
- Own messages: light blue background (#e7f1ff)
- Others' messages: white/gray background (#f8f9fa)
- **Fonts:** System fonts (Bootstrap defaults)
- **Icons:** Bootstrap Icons
---
## 7. Configuration
### 7.1 Environment Variables
| Variable | Description | Default Value |
|----------|-------------|---------------|
| `MC_SERIAL_PORT` | Path to serial device | `/dev/ttyUSB0` |
| `MC_DEVICE_NAME` | Device name (for .msgs file) | `MeshCore` |
| `MC_CONFIG_DIR` | meshcore configuration directory | `/root/.config/meshcore` |
| `MC_REFRESH_INTERVAL` | Refresh interval (seconds) | `60` |
| `MC_INACTIVE_HOURS` | Inactivity hours for cleanup | `48` |
| `FLASK_HOST` | Listen address | `0.0.0.0` |
| `FLASK_PORT` | Application port | `5000` |
| `FLASK_DEBUG` | Debug mode | `false` |
### 7.2 Example .env File
```env
# MeshCore device configuration
MC_SERIAL_PORT=/dev/serial/by-id/usb-Espressif_Systems_heltec_wifi_lora_32_v4__16_MB_FLASH__2_MB_PSRAM__90706984A000-if00
MC_DEVICE_NAME=MarWoj
MC_CONFIG_DIR=/root/.config/meshcore
# Application settings
MC_REFRESH_INTERVAL=60
MC_INACTIVE_HOURS=48
# Flask
FLASK_HOST=0.0.0.0
FLASK_PORT=5000
FLASK_DEBUG=false
```
### 7.3 Docker Compose
```yaml
version: '3.8'
services:
mc-webui:
build: .
container_name: mc-webui
restart: unless-stopped
ports:
- "5000:5000"
devices:
- "${MC_SERIAL_PORT}:${MC_SERIAL_PORT}"
volumes:
- "${MC_CONFIG_DIR}:/root/.config/meshcore:rw"
environment:
- MC_SERIAL_PORT
- MC_DEVICE_NAME
- MC_REFRESH_INTERVAL
- MC_INACTIVE_HOURS
- FLASK_HOST
- FLASK_PORT
env_file:
- .env
```
---
## 8. Implementation Plan
### Phase 0: Environment Setup (0.5 day)
- [ ] Create GitHub repository
- [ ] Project directory structure
- [ ] Docker and docker-compose configuration
- [ ] Install meshcore-cli in container
- [ ] Verify device connection
### Phase 1: Backend - Basics (1 day)
- [ ] Flask application skeleton
- [ ] Configuration from environment variables
- [ ] meshcli wrapper (subprocess)
- [ ] .msgs file parser
- [ ] Basic API endpoints:
- `GET /api/messages` - message list
- `POST /api/messages` - send message
- `GET /api/status` - connection status
### Phase 2: Frontend - Chat View (1 day)
- [ ] Base template (Bootstrap 5)
- [ ] Message list view
- [ ] Distinguish own/others' messages
- [ ] Time formatting
- [ ] Emoji support
### Phase 3: Frontend - Message Sending (0.5 day)
- [ ] Send form
- [ ] Validation
- [ ] Feedback (toast notifications)
- [ ] Reply button (@[UserName])
### Phase 4: Auto-refresh (0.5 day)
- [ ] JavaScript polling
- [ ] Last refresh indicator
- [ ] Manual refresh button
- [ ] Smart scroll (don't jump if user is scrolling)
### Phase 5: Contact Management (0.5 day)
- [ ] Settings panel (modal)
- [ ] Contact cleanup function
- [ ] Action confirmation
- [ ] Result feedback
### Phase 6: Polish & Documentation (0.5 day)
- [ ] Manual testing
- [ ] Error handling and edge cases
- [ ] README with installation instructions
- [ ] Docker image optimization
**Total estimated time: ~4-5 working days**
---
## 9. Future Extensions (Backlog)
Features to consider in future versions:
| Priority | Feature | Description |
|----------|---------|-------------|
| HIGH | Dark mode | Dark theme interface |
| HIGH | Other channels | Send to channels other than Public |
| MEDIUM | Contact list | Browse and manage contacts |
| MEDIUM | Private messages | Send DMs to contacts |
| MEDIUM | Notifications | Audio/visual notifications for new messages |
| MEDIUM | Map | Display contact locations on map |
| LOW | Statistics | Message count, network activity |
| LOW | Telemetry | Display telemetry data |
| LOW | Authentication | Optional login (for internet access) |
| LOW | Multi-device | Support for multiple devices |
---
## 10. Risks and Limitations
### 10.1 Technical Risks
| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| meshcli delays | Medium | Medium | Timeout + async execution |
| Device connection loss | Low | High | Health check + restart |
| Large .msgs file | Low | Medium | Pagination, tail last N |
| Concurrent access conflicts | Low | Low | Command queuing |
### 10.2 Limitations
1. **Single-user** - no multi-user support
2. **No authentication** - requires trusted network
3. **meshcli dependency** - requires installed meshcore-cli
4. **Public channel only (MVP)** - other channels in future versions
5. **No persistence** - data only in .msgs file
### 10.3 Assumptions
1. Heltec V4 device is always connected and available
2. meshcore-cli is properly configured
3. .msgs file is regularly updated by meshcore-cli
4. Local network is trusted (no authentication needed)
---
## 11. Definitions and Abbreviations
| Term | Definition |
|------|------------|
| MeshCore | Mesh networking protocol for LoRa radios |
| meshcore-cli | Command-line interface for MeshCore |
| meshcli | Alias for meshcore-cli |
| Heltec V4 | Heltec WiFi LoRa 32 V4 - LoRa device |
| .msgs | JSON Lines file with message history |
| Public | Default public channel (channel 0) |
| SNR | Signal-to-Noise Ratio |
---
## 12. Changelog
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | 2025-01-21 | Marek/Claude | Initial version |
---
## Appendices
### A. Example Message Format (.msgs)
```json
{"type": "CHAN", "SNR": 10.5, "channel_idx": 0, "path_len": 5, "txt_type": 0, "sender_timestamp": 1766300840, "text": "MarioTJE🤖: Hey everyone ", "name": "channel 0", "timestamp": 1766300846}
{"type": "SENT_CHAN", "channel_idx": 0, "text": "Good morning everyone", "txt_type": 0, "name": "MarWoj", "timestamp": 1766309432}
```
### B. meshcli Commands Used in Project
```bash
# Sync messages
meshcli -s <PORT> recv
# Send to Public
meshcli -s <PORT> public "message"
# Contact list
meshcli -s <PORT> contacts
# Clean inactive contacts (type client, >48h)
meshcli -s <PORT> "apply_to u<48h,t=1 remove_contact"
# Device information
meshcli -s <PORT> infos
```

View File

@@ -0,0 +1,95 @@
# mc-webui - Project Instructions for Claude Code
## Project Overview
**mc-webui** is a lightweight Flask web interface for meshcore-cli. It provides browser-based access to MeshCore mesh network chat without requiring SSH/terminal access.
## Tech Stack
- Python 3.11+ / Flask
- Bootstrap 5 / vanilla JS
- Docker deployment
- subprocess calls to meshcli
## Key Commands
```bash
# meshcli base command (serial port varies per setup)
meshcli -s $MC_SERIAL_PORT <command>
# Common commands:
recv # Fetch new messages
public "message" # Send to Public channel
public "@[User] msg" # Reply to user
contacts # List contacts
"apply_to u<48h,t=1 remove_contact" # Clean old contacts
infos # Device info
```
## Message File Format
Location: `~/.config/meshcore/<device_name>.msgs` (JSON Lines)
```json
{"type": "CHAN", "text": "User: message", "timestamp": 1766300846}
{"type": "SENT_CHAN", "text": "my message", "name": "DeviceName", "timestamp": 1766309432}
```
## Code Style Guidelines
- **Language:** All code, comments, docs in English
- **Simplicity:** MVP approach, avoid over-engineering
- **Error handling:** meshcli can timeout/fail - handle gracefully
- **No auth:** Trusted network assumption
## Environment Variables
```
MC_SERIAL_PORT - Serial device path
MC_DEVICE_NAME - Device name (for .msgs file)
MC_CONFIG_DIR - meshcore config directory
MC_REFRESH_INTERVAL - Auto-refresh seconds (default: 60)
MC_INACTIVE_HOURS - Contact cleanup threshold (default: 48)
FLASK_PORT - Web server port (default: 5000)
```
## Project Structure
```
mc-webui/
├── app/
│ ├── main.py # Flask entry point
│ ├── config.py # Environment config
│ ├── meshcore/
│ │ ├── cli.py # meshcli subprocess wrapper
│ │ └── parser.py # .msgs file parser
│ ├── routes/
│ │ ├── api.py # REST endpoints
│ │ └── views.py # HTML views
│ ├── static/ # CSS, JS
│ └── templates/ # Jinja2 templates
├── docker/
│ ├── Dockerfile
│ └── docker-compose.yml
├── PRD.md # Full requirements doc
└── README.md
```
## API Endpoints
```
GET /api/messages - List messages
POST /api/messages - Send message
GET /api/status - Connection status
POST /api/contacts/cleanup - Remove inactive contacts
```
## Important Files
- **PRD.md** - Complete requirements, wireframes, architecture
- **CLAUDE_CODE_PROMPT.md** - Detailed implementation guide
## Testing
Device: Heltec V4 connected via USB to this Debian VM.
Test with real meshcore network - no mock available locally.

342
technotes/meshcore-cli.md Normal file
View File

@@ -0,0 +1,342 @@
# meshcore-cli
meshcore-cli : CLI interface to MeschCore companion app over BLE, TCP or Serial
## About
meshcore-cli is a tool that connects to your companion radio node (meshcore client) over BLE, TCP or Serial and lets you interact with it from a terminal using a command line interface.
You can send commands as parameters to the meshcore-cli command (from your shell) either interactively or through a script.
There is also an interactive mode (this is the default when no command is passed). In interactive mode you can enter a contact (another client a repeater, a sensor or a room) and interact with it. For clients, interaction consists in sending/receiving messages. For repeaters, rooms or sensors it will directly give you the remote cli (you can still send messages to rooms using double quote prefix or msg command).
Note that meshcore-cli only interacts with companion radios (through BLE, Serial or TCP), you can't connect to a repeater using its serial interface.
Also, most meshcore companions only have one interface compiled in at a time. So you can't connect via Serial to a node, which has been compiled as a BLE companion.
## Install
Meshcore-cli depends on the [python meshcore](https://github.com/fdlamotte/meshcore_py) package. You can install both via `pip` or `pipx` using the command:
<pre>
$ pipx install meshcore-cli
</pre>
It will install you `meshcore-cli` and `meshcli`, which is an alias to the former.
You can use the flake under [nix](https://nixos.org/):
<pre>
$ nix run github:meshcore-dev/meshcore-cli#meshcore-cli
</pre>
If you want meshcore-cli to remember last BLE device, you should have some `$HOME/.config/meshcore` where configuration for meschcore-cli will be stored (if not it will use first device it finds).
## Usage
<pre>
$ meshcli &lt;args&gt; &lt;commands&gt;
</pre>
If using BLE, don't forget to pair your device first (using `bluetoothctl` for instance on Linux) or meshcli won't be able to communicate. There is a device selector for BLE, you'll just have to use `meshcli -S` to select your device, subsequent calls to meshcli will be send to that device.
### Configuration
Configuration files are stored in `$HOME/.config/meshcore`
If the directory exists, default ble address and history will be stored there.
If there is an initialization script file called `init`, it will be executed just before the commands provided on command line are executed (and after evaluation of the arguments).
Init files can also be defined for a given device, meshcore-cli will look for `&lt;device-name>.init` file in configuration directory (usefull to specify timeout for contacts that are behind bridges with `contact_timeout` command).
### Arguments
Arguments mostly deals with connection to the node
<pre>
-h : prints this help
-v : prints version
-j : json output (disables init file)
-D : debug
-S : scan for devices and show a selector
-l : list available ble/serial devices and exit
-T &lt;timeout&gt; : timeout for the ble scan (-S and -l) default 2s
-a &lt;address&gt; : specifies device address (can be a name)
-d &lt;name&gt; : filter meshcore devices with name or address
-P : forces pairing via the OS
-t &lt;hostname&gt; : connects via tcp/ip
-p &lt;port&gt; : specifies tcp port (default 5000)
-s &lt;port&gt; : use serial port &lt;port&gt;
-b &lt;baudrate&gt; : specify baudrate
-C : toggles classic mode for prompt
-c &lt;on/off&gt; : disables most of color output if off
</pre>
### Available Commands
Commands are given after arguments, they can be chained and some have shortcuts. Also prefixing a command with a dot `.` will force it to output json instead of synthetic result.
<pre>
?&lt;cmd&gt; may give you some more help about cmd
General commands
chat : enter the chat (interactive) mode
chat_to &lt;ct&gt; : enter chat with contact to
script &lt;filename&gt; : execute commands in filename
infos : print informations about the node i
self_telemetry : print own telemtry t
card : export this node URI e
ver : firmware version v
reboot : reboots node
sleep &lt;secs&gt; : sleeps for a given amount of secs s
wait_key : wait until user presses &lt;Enter&gt; wk
apply_to &lt;f&gt; &lt;cmds&gt; : sends cmds to contacts matching f at
Messaging
msg &lt;name&gt; &lt;msg&gt; : send message to node by name m {
wait_ack : wait an ack wa }
chan &lt;nb&gt; &lt;msg&gt; : send message to channel number &lt;nb&gt; ch
public &lt;msg&gt; : send message to public channel (0) dch
recv : reads next msg r
wait_msg : wait for a message and read it wm
sync_msgs : gets all unread msgs from the node sm
msgs_subscribe : display msgs as they arrive ms
get_channels : prints all channel info
get_channel &lt;n&gt; : get info for channel (by number or name)
set_channel n nm k : set channel info (nb, name, key)
remove_channel &lt;n&gt; : remove channel (by number or name)
scope &lt;s&gt; : sets node's flood scope
Management
advert : sends advert a
floodadv : flood advert
get &lt;param&gt; : gets a param, \"get help\" for more
set &lt;param&gt; &lt;value&gt; : sets a param, \"set help\" for more
time &lt;epoch&gt; : sets time to given epoch
clock : get current time
clock sync : sync device clock st
node_discover &lt;filter&gt; : discovers nodes based on their type nd
Contacts
contacts / list : gets contact list lc
reload_contacts : force reloading all contacts rc
contact_info &lt;ct&gt; : prints information for contact ct ci
contact_timeout &lt;ct&gt; v : sets temp default timeout for contact
share_contact &lt;ct&gt; : share a contact with others sc
export_contact &lt;ct&gt; : get a contact's URI ec
import_contact &lt;URI&gt; : import a contact from its URI ic
remove_contact &lt;ct&gt; : removes a contact from this node
path &lt;ct&gt; : diplays path for a contact
disc_path &lt;ct&gt; : discover new path and display dp
reset_path &lt;ct&gt; : resets path to a contact to flood rp
change_path &lt;ct&gt; &lt;pth&gt; : change the path to a contact cp
change_flags &lt;ct&gt; &lt;f&gt; : change contact flags (tel_l|tel_a|star)cf
req_telemetry &lt;ct&gt; : prints telemetry data as json rt
req_mma &lt;ct&gt; : requests min/max/avg for a sensor rm
req_acl &lt;ct&gt; : requests access control list for sensor
pending_contacts : show pending contacts
add_pending &lt;pending&gt; : manually add pending contact
flush_pending : flush pending contact list
Repeaters
login &lt;name&gt; &lt;pwd&gt; : log into a node (rep) with given pwd l
logout &lt;name&gt; : log out of a repeater
cmd &lt;name&gt; &lt;cmd&gt; : sends a command to a repeater (no ack) c [
wmt8 : wait for a msg (reply) with a timeout ]
req_status &lt;name&gt; : requests status from a node rs
req_neighbours &lt;name&gt; : requests for neighbours in binary form rn
trace &lt;path&gt; : run a trace, path is comma separated
</pre>
### Interactive Mode
aka Instant Message or chat mode ...
Chat mode lets you interactively interact with your node or remote nodes. It is automatically triggered when no option is given on the command line.
You'll get a prompt with the name of your node. From here you can type meshcore-cli commands. The prompt has history and a basic completion (pressing tab will display possible command or argument options).
The `to` command is specific to chat mode, it lets you enter the recipient for next command. By default you're on your node but you can enter other nodes or public rooms. Here are some examples :
- `to <dest>` : will enter dest (node or channel)
- `to /`, `to ~` : will go to the root (your node)
- `to ..` : will go to the last node (it will switch between the two last nodes, this is just a 1-depth history)
- `to !` : will switch to the node you received last message from
When you are in a node, the behaviour will depend on the node type, if you're on a chat node, it will send messages by default and you can chat. On a repeater or a room server, it will send commands (autocompletion has been set to comply with the CommonCli class of meshcore). To send a message through a room you'll have to prefix the message with a quote or use the send command.
The `/` character is used to bypass the node you have currently selected using `to`:
- `/<cmd>` issues cmd command on the root
- `/<node>/<cmd>` will send cmd to selected node
- `/<dest> <msg>` will send msg to dest (channel or node)
#### Flood Scope in interactive mode
Flood scope has recently been introduced in meshcore (from `v1.10.0`). It limits the scope of packets to regions, using transport codes in the frame.
When entering chat mode, scope will be reset to `*`, meaning classic flood.
You can switch scope using the `scope` command, or postfixing the `to` command with `%<scope>`.
Scope can also be applied to a command using `%` before the scope name. For instance `login%#Morbihan` will limit diffusion of the login command (which is usually sent flood to get the path to a repeater) to the `#Morbihan` region.
#### Channel echoes
It's sometimes interesting to know the path taken by a message received from a channel or which repeaters have repeated a sent message.
The app give you the information by listening `rx_log` from the device, when obtained the information is attached to the message and can be read.
In meshcore-cli I went lower-level by implementing channel echoes. When activated (with `/set channel_echoes on`), all the channel messages will be printed on the terminal along with the SNR and path taken. When sending a message, you'll have all the repeats from 0-hop repeaters as echoes, and when a message is received, you should see information about the received message, but also all the instances of the same message that might have reached you from another path.
In the example below, a msg has been sent between two repeaters, 21 and 25. 25 repeated the message and 21 the repeat and both echoes came back to the node with different SNRs.
```
f1down/#fdl|*> 8
#fdl f1down: 8 [25] -4.75-112
#fdl f1down: 8 [2521] 1.00-109
```
### Contact management
To receive a message from another user, it is necessary to have its public key. This key is stored on a contact list in the device, and this list has a finite size (50 when meshcore started, now over 350 for most devices).
By default contacts are automatically added to the device contact list when an advertisement is received, so as soon as you receive an advert, you can talk with your buddy.
With growing number of users, it becomes necessary to manage contact list and one of the ways is to add contacts manually to the device. This is done by turning on `manual_add_contacts`. Once this option has been turned on, a pending list is built by meshcore-cli from the received adverts. You can view the list issuing a `pending_contacts` command, flush the list using `flush_pending` or add a contact from the list with `add_pending` followed by the key of the contact or its name (both will be auto-completed with tab).
This feature only really works in interactive mode.
Note: There is also an `auto_update_contacts` setting that has nothing to do with adding contacts, it permits to automatically sync contact lists between device and meshcore-cli (when there is an update in name, location or path).
### Issuing batch commands to contacts with apply to
`apply_to <f> <cmd>` : applies cmd to contacts matching filter `<f>` it can be used to apply the same command to a pool of repeaters, or remove some contacts matching a condition.
Filter is constructed with comma separated fields :
- `u`, matches modification time `<` or `>` than a timestamp (can also be days hours or minutes ago if followed by `d`,`h` or `m`)
- `t`, matches the type (1: client, 2: repeater, 3: room, 4: sensor)
- `h`, matches number of hops
- `d`, direct, similar to `h>-1`
- `f`, flood, similar to `h<0` or `h=-1`
Commands should be written as if in interactive mode, if writing from the commandline don't forget to use commas to clearly delimit fields.
Note: Some commands like `contact_name` (aka `cn`), `reset_path` (aka `rp`), `forget_password` (aka `fp`) can be chained. There is also a `sleep` command taking an optional time parameter. The sleep will be issued after the command, it helps limiting rate through repeaters ...
#### Examples
```
# removes all clients that have not been updated in last 2 days
at u<2d,t=1 remove_contact
# gives traces to repeaters that have been updated in the last 24h and are direct
at t=2,u>1d,d cn trace
# tries to do flood login to all repeaters
at t=2 rp login
```
## Examples
<pre>
# gets info from first ble MC device it finds (was -s but now used for serial port)
$ meshcore-cli -d "" infos
INFO:meshcore:Scanning for devices
INFO:meshcore:Found device : C2:2B:A1:D5:3E:B6: MeshCore-t114_fdl
INFO:meshcore:BLE Connection started
{
"adv_type": 1,
"tx_power": 22,
"max_tx_power": 22,
"public_key": "993acd42fc779962c68c627829b32b111fa27a67d86b75c17460ff48c3102db4",
"adv_lat": 47.794,
"adv_lon": -3.428,
"radio_freq": 869.525,
"radio_bw": 250.0,
"radio_sf": 11,
"radio_cr": 5,
"name": "t114_fdl"
}
# getting time
$ meshcli -a C2:2B:A1:D5:3E:B6 clock
INFO:meshcore:BLE Connection started
Current time : 2025-04-18 08:19:26 (1744957166)
# If you're familiar with meshcli, you should have noted that
# now output is not json only, to get json output, use -j
# or prefix your commands with a dot
$ meshcli -a C2:2B:A1:D5:3E:B6 .clock
INFO:meshcore:BLE Connection started
{
"time": 1744957249
}
# Using -j, meshcli will return replies in json format ...
$ meshcli -j -a C2:2B:A1:D5:3E:B6 clock
{
"time": 1744957261
}
# So if I reboot the node, and want to set time, I can chain the commands
# and get that kind of output (even better by feeding it to jq)
$ meshcli reboot
INFO:meshcore:BLE Connection started
$ meshcli -j clock clock sync clock | jq -c
{ "time": 1715770371 }
{ "ok": "time synced" }
{ "time": 1745996105 }
# Now check if time is ok with human output (I don't read epoch time yet)
$ meshcli clock
INFO:meshcore:BLE Connection started
Current time : 2025-04-30 08:56:27 (1745996187)
# Now you'll probably want to send some messages ...
# For that, there is the msg command, wait_ack
$ meshcli msg Techo_fdl "Hello T-Echo" wa
INFO:meshcore:BLE Connection started
Msg acked
# I can check the message on the techo
$ meshcli -d Techo sm
INFO:meshcore:Scanning for devices
INFO:meshcore:Found device : DE:B6:D0:68:D5:62: MeshCore-Techo_fdl
INFO:meshcore:BLE Connection started
t114_fdl(0): Hello T-Echo
# And reply using json output for more verbosity
# here I've used jq with -cs to get a compact array
$ meshcli msg t114_fdl hello wa | jq -cs
[{"type":0,"expected_ack":"4802ed93","suggested_timeout":2970},{"code":"4802ed93"}]
# But this could have been done interactively using the chat mode
# Here from the techo side. Note that un-acked messages will be
# signaled with an ! at the start of the prompt (or red color in color mode)
$ meshcli chat
INFO:meshcore:BLE Connection started
Interactive mode, most commands from terminal chat should work.
Use "to" to selects contact, "list" to list contacts, "send" to send a message ...
Line starting with "$" or "." will issue a meshcli command.
"quit" or "q" will end interactive mode
t114_fdl(D): Hello T-Echo
EnsibsRoom> Hi
!EnsibsRoom> to t114_fdl
t114_fdl> Hi
t114_fdl(D): It took you long to reply ...
t114_fdl> I forgot to set the recipient with the to command
t114_fdl(D): It happens ...
t114_fdl>
# Loging into repeaters and sending commands is also possible
# directly from the chat, because we can use meshcli commands ;)
$ meshcli chat (pending msgs are shown at connexion ...)
INFO:meshcore:BLE Connection started
Interactive mode, most commands from terminal chat should work.
Use "to" to selects contact, "list" to list contacts, "send" to send a message ...
Line starting with "$" or "." will issue a meshcli command.
"quit" or "q" will end interactive mode
Techo_fdl(0): Cool to receive some msgs from you
Techo_fdl(D): Hi
Techo_fdl(D): I forgot to set the recipient with the to command
FdlRoom> login password
Login success
FdlRoom> clock
FdlRoom(0): 06:40 - 18/4/2025 UTC
FdlRoom>
</pre>