diff --git a/routes/meshtastic.py b/routes/meshtastic.py index f8629d6..0f78b69 100644 --- a/routes/meshtastic.py +++ b/routes/meshtastic.py @@ -57,6 +57,38 @@ def _message_callback(msg: MeshtasticMessage) -> None: pass +@meshtastic_bp.route('/ports') +def list_ports(): + """ + List available serial ports that may have Meshtastic devices. + + Returns: + JSON with list of available serial ports. + """ + if not is_meshtastic_available(): + return jsonify({ + 'status': 'error', + 'ports': [], + 'message': 'Meshtastic SDK not installed' + }) + + try: + from meshtastic.util import findPorts + ports = findPorts() + return jsonify({ + 'status': 'ok', + 'ports': ports, + 'count': len(ports) + }) + except Exception as e: + logger.error(f"Error listing ports: {e}") + return jsonify({ + 'status': 'error', + 'ports': [], + 'message': str(e) + }) + + @meshtastic_bp.route('/status') def get_status(): """ diff --git a/static/js/modes/meshtastic.js b/static/js/modes/meshtastic.js index 8efcdcc..133eb5d 100644 --- a/static/js/modes/meshtastic.js +++ b/static/js/modes/meshtastic.js @@ -24,9 +24,43 @@ const Meshtastic = (function() { */ function init() { initMap(); + loadPorts(); checkStatus(); } + /** + * Load available serial ports and populate dropdown + */ + async function loadPorts() { + try { + const response = await fetch('/meshtastic/ports'); + const data = await response.json(); + + const select = document.getElementById('meshStripDevice'); + if (!select) return; + + // Clear existing options except auto-detect + select.innerHTML = ''; + + if (data.status === 'ok' && data.ports && data.ports.length > 0) { + data.ports.forEach(port => { + const option = document.createElement('option'); + option.value = port; + option.textContent = port; + select.appendChild(option); + }); + + // If multiple ports, select the first one by default to avoid auto-detect failure + if (data.ports.length > 1) { + select.value = data.ports[0]; + showStatusMessage(`Multiple ports detected. Selected ${data.ports[0]}`, 'warning'); + } + } + } catch (err) { + console.error('Failed to load ports:', err); + } + } + /** * Initialize the Leaflet map */ @@ -91,7 +125,15 @@ const Meshtastic = (function() { // Try strip device select first, then sidebar const stripDeviceSelect = document.getElementById('meshStripDevice'); const sidebarDeviceSelect = document.getElementById('meshDeviceSelect'); - const device = stripDeviceSelect?.value || sidebarDeviceSelect?.value || null; + let device = stripDeviceSelect?.value || sidebarDeviceSelect?.value || null; + + // Check if auto-detect is selected but multiple ports exist + if (!device && stripDeviceSelect && stripDeviceSelect.options.length > 2) { + // Multiple ports available - prompt user to select one + showStatusMessage('Multiple ports detected. Please select a specific device from the dropdown.', 'warning'); + updateStatusIndicator('disconnected', 'Select a device'); + return; + } updateStatusIndicator('connecting', 'Connecting...'); @@ -1164,6 +1206,7 @@ const Meshtastic = (function() { init, start, stop, + loadPorts, refreshChannels, openChannelModal, closeChannelModal,