mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-05-06 21:42:30 +02:00
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:
18
README.md
18
README.md
@@ -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
|
||||
|
||||
@@ -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)
|
||||
# =============================================================================
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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
|
||||
// =============================================================================
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user