mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-03-28 17:42:45 +01:00
feat(contacts): show path/route info in UI and split console commands
- Console `contacts` now shows device-only contacts with path info (matching meshcore-cli format: name, type, pubkey, path) - New `contacts_all` command shows all contacts (device + cached from DB) - Contact cards in UI now always show routing mode for device contacts (Flood, Direct 0 hop, or hex path with hop count) - Fix path_or_mode computation: prioritize out_path over out_path_len to handle firmware edge case where out_path exists but out_path_len=-1 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
35
app/main.py
35
app/main.py
@@ -216,15 +216,41 @@ def _execute_console_command(args: list) -> str:
|
||||
return "No device info available"
|
||||
|
||||
elif cmd == 'contacts':
|
||||
# Show device-only contacts with path info (like meshcore-cli)
|
||||
type_names = {0: 'NONE', 1: 'CLI', 2: 'REP', 3: 'ROOM', 4: 'SENS'}
|
||||
if not device_manager.mc or not device_manager.mc.contacts:
|
||||
return "No contacts on device"
|
||||
try:
|
||||
device_manager.execute(device_manager.mc.ensure_contacts(follow=True))
|
||||
except Exception:
|
||||
pass # use whatever is in memory
|
||||
lines = []
|
||||
for pk, c in device_manager.mc.contacts.items():
|
||||
name = c.get('adv_name', c.get('name', '?'))
|
||||
typ = type_names.get(c.get('type', 1), '?')
|
||||
pk_short = pk[:12]
|
||||
opl = c.get('out_path_len', -1)
|
||||
if opl == -1:
|
||||
path_str = 'Flood'
|
||||
elif opl == 0:
|
||||
path_str = '0 hop'
|
||||
else:
|
||||
path_str = c.get('out_path', f'len:{opl}')
|
||||
lines.append(f" {name:30} {typ:4} {pk_short} {path_str}")
|
||||
return f"Contacts ({len(lines)}) on device:\n" + "\n".join(lines)
|
||||
|
||||
elif cmd == 'contacts_all':
|
||||
# Show all known contacts (device + cached from DB)
|
||||
contacts = device_manager.get_contacts_from_device()
|
||||
if not contacts:
|
||||
return "No contacts"
|
||||
lines = []
|
||||
for c in contacts:
|
||||
name = c.get('name', '?')
|
||||
pk = c.get('public_key', '')[:8]
|
||||
lines.append(f" {name} ({pk}...)")
|
||||
return f"Contacts ({len(contacts)}):\n" + "\n".join(lines)
|
||||
pk = c.get('public_key', '')[:12]
|
||||
source = c.get('source', '')
|
||||
lines.append(f" {name} ({pk}...) [{source}]")
|
||||
return f"All contacts ({len(contacts)}):\n" + "\n".join(lines)
|
||||
|
||||
elif cmd == 'bat':
|
||||
bat = device_manager.get_battery()
|
||||
@@ -356,7 +382,8 @@ def _execute_console_command(args: list) -> str:
|
||||
" status — Connection status, battery, contacts count\n"
|
||||
" stats — Device statistics (uptime, TX/RX, packets)\n"
|
||||
" bat — Battery voltage\n"
|
||||
" contacts — List all contacts\n"
|
||||
" contacts — List device contacts with path info\n"
|
||||
" contacts_all — List all known contacts (device + cached)\n"
|
||||
" channels — List configured channels\n"
|
||||
" chan <idx> <msg> — Send channel message\n"
|
||||
" msg <name> <msg> — Send direct message\n"
|
||||
|
||||
@@ -2378,10 +2378,13 @@ def get_contacts_detailed_api():
|
||||
# Compute path display string
|
||||
out_path_len = details.get('out_path_len', -1)
|
||||
out_path = details.get('out_path', '')
|
||||
if out_path_len == -1:
|
||||
path_or_mode = 'Flood'
|
||||
elif out_path:
|
||||
if out_path:
|
||||
# out_path present = known route (even if out_path_len says -1)
|
||||
path_or_mode = out_path
|
||||
elif out_path_len == -1:
|
||||
path_or_mode = 'Flood'
|
||||
elif out_path_len == 0:
|
||||
path_or_mode = '0 hop'
|
||||
else:
|
||||
path_or_mode = f'Path len: {out_path_len}'
|
||||
|
||||
|
||||
@@ -2142,12 +2142,22 @@ function createExistingContactCard(contact, index) {
|
||||
lastAdvertDiv.appendChild(timeText);
|
||||
}
|
||||
|
||||
// Path/mode (optional)
|
||||
// Path/route info for device contacts
|
||||
let pathDiv = null;
|
||||
if (contact.path_or_mode && contact.path_or_mode !== 'Flood') {
|
||||
if (contact.on_device !== false) {
|
||||
pathDiv = document.createElement('div');
|
||||
pathDiv.className = 'text-muted small';
|
||||
pathDiv.textContent = `Path: ${contact.path_or_mode}`;
|
||||
const mode = contact.path_or_mode || 'Flood';
|
||||
const pathLen = contact.out_path_len;
|
||||
if (mode === 'Flood') {
|
||||
pathDiv.innerHTML = '<i class="bi bi-broadcast"></i> Flood';
|
||||
} else if (mode === '0 hop') {
|
||||
pathDiv.innerHTML = '<i class="bi bi-arrow-right-short"></i> Direct (0 hop)';
|
||||
} else {
|
||||
// mode is hex path string, show hops count + path
|
||||
const hops = pathLen >= 0 ? pathLen : '?';
|
||||
pathDiv.innerHTML = `<i class="bi bi-signpost-split"></i> Path: ${mode} (${hops} hops)`;
|
||||
}
|
||||
}
|
||||
|
||||
// Action buttons
|
||||
|
||||
Reference in New Issue
Block a user