fix: Remove non-functional Node Discovery feature

Removed Node Discovery feature that was experiencing persistent timeout
issues. Feature attempted to scan mesh network for nearby repeaters but
consistently failed due to bridge timing constraints.

Changes:
- Remove node_discover() function from cli.py
- Remove 'node_discover' from SPECIAL_COMMANDS in api.py
- Remove Discover Nodes button and modal from base.html
- Remove discoverNodes() and displayNodeDiscoveryResults() from app.js
- Remove Discover Nodes documentation from README.md

IMPORTANT: Advert message cleanup ("Advert sent") is preserved and
working correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-01-03 10:46:05 +01:00
parent df26b44df0
commit 66f16609e5
5 changed files with 16 additions and 292 deletions

View File

@@ -521,24 +521,6 @@ Sends a single advertisement frame to announce your node's presence in the mesh
2. Click "Send Advert" under Network Commands
3. Wait for confirmation toast
#### Discover Nodes
Scans the mesh network to find nearby repeaters and displays their signal quality. Useful for network diagnostics and finding the best repeater connections.
1. Click the menu icon (☰) in the navbar
2. Click "Discover Nodes" under Network Commands
3. A modal window opens showing nearby repeaters with:
- **Node ID**: Public key prefix and tag
- **SNR (Signal-to-Noise Ratio)**: Higher is better (>10dB = excellent, 5-10dB = good, <5dB = poor)
- **RSSI**: Received signal strength in dBm
- **SNR In**: Inbound signal quality
- **Hops**: Number of hops to reach the node
4. Click "Refresh" to rescan for nodes
Results are sorted by signal strength (strongest first) and color-coded:
- 🟢 **Green**: SNR ≥ 10dB (excellent connection)
- 🟡 **Yellow**: SNR 5-10dB (good connection)
- 🔴 **Red**: SNR < 5dB (poor connection)
#### Flood Advert (Use Sparingly!)
Sends advertisement in flooding mode, forcing all nodes to retransmit. **Use only when:**
- Starting a completely new network

View File

@@ -368,60 +368,6 @@ def floodadv() -> Tuple[bool, str]:
return success, stdout or stderr
def node_discover() -> Tuple[bool, List[Dict]]:
"""
Discover nearby mesh nodes (repeaters).
Uses .node_discover command which returns JSON array of nearby repeaters
with SNR, RSSI, and other metadata.
Returns:
Tuple of (success, nodes_list)
Each node dict contains:
{
'SNR': float,
'RSSI': int,
'path_len': int,
'node_type': int (2=REP),
'SNR_in': float,
'tag': str (hex),
'pubkey': str (hex)
}
"""
success, stdout, stderr = _run_command(['.node_discover'])
if not success:
logger.error(f"node_discover failed: {stderr}")
return False, []
try:
# Clean output: remove prompt lines (e.g., "MarWoj|* .node_discover")
# and extract only the JSON array
cleaned_output = stdout.strip()
# Find the start of JSON array (first '[')
json_start = cleaned_output.find('[')
if json_start == -1:
logger.error(f"No JSON array found in output: {stdout}")
return False, []
# Extract JSON from first '[' to end
json_str = cleaned_output[json_start:]
# Parse JSON array
nodes = json.loads(json_str)
if not isinstance(nodes, list):
logger.error(f"node_discover returned non-array: {stdout}")
return False, []
logger.info(f"Discovered {len(nodes)} nearby nodes")
return True, nodes
except json.JSONDecodeError as e:
logger.error(f"Failed to parse node_discover JSON: {e}, output: {stdout}")
return False, []
# =============================================================================
# Direct Messages (DM)
# =============================================================================

View File

@@ -577,10 +577,6 @@ SPECIAL_COMMANDS = {
'function': cli.floodadv,
'description': 'Flood advertisement (use sparingly!)',
},
'node_discover': {
'function': cli.node_discover,
'description': 'Discover nearby mesh nodes (repeaters)',
},
}
@@ -614,57 +610,26 @@ def execute_special_command():
# Execute the command
cmd_info = SPECIAL_COMMANDS[command]
result = cmd_info['function']()
success, message = cmd_info['function']()
# Handle different return types
if command == 'node_discover':
# node_discover returns (success, nodes_list)
success, nodes = result
if success:
return jsonify({
'success': True,
'command': command,
'nodes': nodes,
'count': len(nodes)
}), 200
else:
return jsonify({
'success': False,
'command': command,
'error': 'Failed to discover nodes'
}), 500
elif command == 'advert':
# advert returns (success, message) - parse to show only "Advert sent"
success, message = result
if success:
# Extract clean message - just use "Advert sent" instead of full output
if success:
# Clean up advert message
if command == 'advert':
clean_message = "Advert sent"
return jsonify({
'success': True,
'command': command,
'message': clean_message
}), 200
else:
return jsonify({
'success': False,
'command': command,
'error': message
}), 500
clean_message = message or f'{command} executed successfully'
return jsonify({
'success': True,
'command': command,
'message': clean_message
}), 200
else:
# Other commands (floodadv) return (success, message)
success, message = result
if success:
return jsonify({
'success': True,
'command': command,
'message': message or f'{command} executed successfully'
}), 200
else:
return jsonify({
'success': False,
'command': command,
'error': message
}), 500
return jsonify({
'success': False,
'command': command,
'error': message
}), 500
except Exception as e:
logger.error(f"Error executing special command: {e}")

View File

@@ -303,17 +303,6 @@ function setupEventListeners() {
}
await executeSpecialCommand('floodadv');
});
// Node Discovery Modal: Load nodes when opened
const nodeDiscoveryModal = document.getElementById('nodeDiscoveryModal');
nodeDiscoveryModal.addEventListener('show.bs.modal', function() {
discoverNodes();
});
// Node Discovery: Refresh button
document.getElementById('refreshDiscoveryBtn').addEventListener('click', function() {
discoverNodes();
});
}
/**
@@ -1264,105 +1253,6 @@ async function copyChannelKey() {
}
/**
* Discover nearby mesh nodes (repeaters)
*/
async function discoverNodes() {
// Show loading state
document.getElementById('nodeDiscoveryStatus').style.display = 'block';
document.getElementById('nodeDiscoveryResults').style.display = 'none';
document.getElementById('nodeDiscoveryError').style.display = 'none';
document.getElementById('nodeDiscoveryEmpty').style.display = 'none';
// Disable refresh button during discovery
const refreshBtn = document.getElementById('refreshDiscoveryBtn');
refreshBtn.disabled = true;
try {
const response = await fetch('/api/device/command', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ command: 'node_discover' })
});
const data = await response.json();
if (data.success && data.nodes) {
displayNodeDiscoveryResults(data.nodes);
} else {
// Show error
document.getElementById('nodeDiscoveryStatus').style.display = 'none';
document.getElementById('nodeDiscoveryError').style.display = 'block';
document.getElementById('nodeDiscoveryErrorMessage').textContent = data.error || 'Failed to discover nodes';
}
} catch (error) {
console.error('Error discovering nodes:', error);
// Show error
document.getElementById('nodeDiscoveryStatus').style.display = 'none';
document.getElementById('nodeDiscoveryError').style.display = 'block';
document.getElementById('nodeDiscoveryErrorMessage').textContent = 'Network error: ' + error.message;
} finally {
refreshBtn.disabled = false;
}
}
/**
* Display node discovery results in table
*/
function displayNodeDiscoveryResults(nodes) {
const tableBody = document.getElementById('nodeDiscoveryTableBody');
// Hide loading state
document.getElementById('nodeDiscoveryStatus').style.display = 'none';
// Check if empty
if (nodes.length === 0) {
document.getElementById('nodeDiscoveryEmpty').style.display = 'block';
return;
}
// Show results table
document.getElementById('nodeDiscoveryResults').style.display = 'block';
// Clear previous results
tableBody.innerHTML = '';
// Sort nodes by SNR (descending - strongest signal first)
nodes.sort((a, b) => (b.SNR || 0) - (a.SNR || 0));
// Populate table
nodes.forEach(node => {
const row = document.createElement('tr');
// Determine signal quality class
let signalClass = '';
const snr = node.SNR || 0;
if (snr >= 10) {
signalClass = 'text-success fw-bold';
} else if (snr >= 5) {
signalClass = 'text-warning';
} else {
signalClass = 'text-danger';
}
row.innerHTML = `
<td>
<span class="font-monospace">${escapeHtml(node.pubkey?.substring(0, 12) || 'unknown')}</span>
${node.tag ? `<br><small class="text-muted">Tag: ${escapeHtml(node.tag)}</small>` : ''}
</td>
<td class="${signalClass}">${snr.toFixed(2)}</td>
<td>${node.RSSI !== undefined ? node.RSSI : 'N/A'}</td>
<td>${node.SNR_in !== undefined ? node.SNR_in.toFixed(2) : 'N/A'}</td>
<td>${node.path_len !== undefined ? node.path_len : 'N/A'}</td>
`;
tableBody.appendChild(row);
});
}
// =============================================================================
// Direct Messages (DM) Functions
// =============================================================================

View File

@@ -85,13 +85,6 @@
<small class="d-block text-muted">Announce presence (normal)</small>
</div>
</button>
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" id="discoverNodesBtn" title="Discover nearby mesh repeaters" data-bs-toggle="modal" data-bs-target="#nodeDiscoveryModal" data-bs-dismiss="offcanvas">
<i class="bi bi-radar" style="font-size: 1.5rem;"></i>
<div>
<span>Discover Nodes</span>
<small class="d-block text-muted">Find nearby repeaters</small>
</div>
</button>
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3 text-warning" id="floodadvBtn" title="Flood advertisement - use sparingly! High airtime usage.">
<i class="bi bi-broadcast" style="font-size: 1.5rem;"></i>
<div>
@@ -249,58 +242,6 @@
</div>
</div>
<!-- Node Discovery Modal -->
<div class="modal fade" id="nodeDiscoveryModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-radar"></i> Nearby Mesh Nodes</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="nodeDiscoveryStatus" class="text-center mb-3">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Discovering nodes...</span>
</div>
<p class="mt-2 text-muted">Scanning for nearby repeaters...</p>
</div>
<div id="nodeDiscoveryResults" style="display: none;">
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead>
<tr>
<th>Node</th>
<th>SNR (dB)</th>
<th>RSSI (dBm)</th>
<th>SNR In (dB)</th>
<th>Hops</th>
</tr>
</thead>
<tbody id="nodeDiscoveryTableBody">
<!-- Populated by JavaScript -->
</tbody>
</table>
</div>
</div>
<div id="nodeDiscoveryError" class="alert alert-warning" style="display: none;">
<i class="bi bi-exclamation-triangle"></i>
<span id="nodeDiscoveryErrorMessage">Failed to discover nodes</span>
</div>
<div id="nodeDiscoveryEmpty" class="text-center text-muted py-3" style="display: none;">
<i class="bi bi-info-circle" style="font-size: 2rem;"></i>
<p class="mt-2">No nearby repeaters found</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="refreshDiscoveryBtn">
<i class="bi bi-arrow-clockwise"></i> Refresh
</button>
</div>
</div>
</div>
</div>
<!-- Toast container for notifications -->
<div class="toast-container position-fixed top-0 start-0 p-3">
<div id="notificationToast" class="toast" role="alert">