diff --git a/app/main.py b/app/main.py
index a2df1a3..d9a2d46 100644
--- a/app/main.py
+++ b/app/main.py
@@ -230,12 +230,19 @@ def _execute_console_command(args: list) -> str:
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'
+ if opl > 0:
+ # Decode path: lower 6 bits = hop count, upper 2 bits = hash_size-1
+ hop_count = opl & 0x3F
+ hash_size = (opl >> 6) + 1
+ raw = c.get('out_path', '')
+ meaningful = raw[:hop_count * hash_size * 2]
+ chunk = hash_size * 2
+ hops = [meaningful[i:i+chunk].upper() for i in range(0, len(meaningful), chunk)]
+ path_str = '→'.join(hops) if hops else f'len:{opl}'
elif opl == 0:
- path_str = '0 hop'
+ path_str = 'Direct'
else:
- path_str = c.get('out_path', f'len:{opl}')
+ path_str = 'Flood'
lines.append(f" {name:30} {typ:4} {pk_short} {path_str}")
return f"Contacts ({len(lines)}) on device:\n" + "\n".join(lines)
diff --git a/app/routes/api.py b/app/routes/api.py
index bf1a013..b2831e4 100644
--- a/app/routes/api.py
+++ b/app/routes/api.py
@@ -2376,17 +2376,23 @@ def get_contacts_detailed_api():
for public_key, details in contacts_detailed.items():
# Compute path display string
+ # out_path_len encodes hop count (lower 6 bits) and hash_size (upper 2 bits)
+ # In MeshCore V1: hash_size=1 byte per hop, so each hop = 2 hex chars
out_path_len = details.get('out_path_len', -1)
- out_path = details.get('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'
+ out_path_raw = details.get('out_path', '')
+ if out_path_len > 0 and out_path_raw:
+ hop_count = out_path_len & 0x3F
+ hash_size = (out_path_len >> 6) + 1 # 1, 2, or 3 bytes per hop
+ # Truncate to meaningful bytes (firmware buffer may have trailing garbage)
+ meaningful_hex = out_path_raw[:hop_count * hash_size * 2]
+ # Format as HEX→HEX→HEX (each hop is hash_size*2 hex chars)
+ chunk = hash_size * 2
+ hops = [meaningful_hex[i:i+chunk].upper() for i in range(0, len(meaningful_hex), chunk)]
+ path_or_mode = '→'.join(hops) if hops else out_path_raw
elif out_path_len == 0:
- path_or_mode = '0 hop'
+ path_or_mode = 'Direct'
else:
- path_or_mode = f'Path len: {out_path_len}'
+ path_or_mode = 'Flood'
contact = {
# All original fields from contact_info
diff --git a/app/static/js/contacts.js b/app/static/js/contacts.js
index 6ab30e6..c9a4fe7 100644
--- a/app/static/js/contacts.js
+++ b/app/static/js/contacts.js
@@ -2148,15 +2148,14 @@ function createExistingContactCard(contact, index) {
pathDiv = document.createElement('div');
pathDiv.className = 'text-muted small';
const mode = contact.path_or_mode || 'Flood';
- const pathLen = contact.out_path_len;
if (mode === 'Flood') {
pathDiv.innerHTML = ' Flood';
- } else if (mode === '0 hop') {
- pathDiv.innerHTML = ' Direct (0 hop)';
+ } else if (mode === 'Direct') {
+ pathDiv.innerHTML = ' Direct';
} else {
- // mode is hex path string, show hops count + path
- const hops = pathLen >= 0 ? pathLen : '?';
- pathDiv.innerHTML = ` Path: ${mode} (${hops} hops)`;
+ // mode is formatted path like "E7→DE→54→54→D8"
+ const hopCount = mode.split('→').length;
+ pathDiv.innerHTML = ` ${mode} (${hopCount} hops)`;
}
}