mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-08-12 03:32:56 +02:00
feat: Auto-detect device name from meshcli prompt
Bridge now detects device name from meshcli prompt ("DeviceName|*")
and exposes it via /health endpoint. mc-webui fetches this at startup
and uses RuntimeConfig for dynamic device name throughout the app.
Fallback chain: prompt detection → .infos command → MC_DEVICE_NAME env var
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ from typing import List, Dict, Optional
|
|||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from apscheduler.triggers.cron import CronTrigger
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
|
|
||||||
from app.config import config
|
from app.config import config, runtime_config
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ def get_archive_path(archive_date: str) -> Path:
|
|||||||
Path to archive file
|
Path to archive file
|
||||||
"""
|
"""
|
||||||
archive_dir = config.archive_dir_path
|
archive_dir = config.archive_dir_path
|
||||||
filename = f"{config.MC_DEVICE_NAME}.{archive_date}.msgs"
|
filename = f"{runtime_config.get_device_name()}.{archive_date}.msgs"
|
||||||
return archive_dir / filename
|
return archive_dir / filename
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ def archive_messages(archive_date: Optional[str] = None) -> Dict[str, any]:
|
|||||||
archive_dir.mkdir(parents=True, exist_ok=True)
|
archive_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Get source .msgs file
|
# Get source .msgs file
|
||||||
source_file = config.msgs_file_path
|
source_file = runtime_config.get_msgs_file_path()
|
||||||
if not source_file.exists():
|
if not source_file.exists():
|
||||||
logger.warning(f"Source messages file not found: {source_file}")
|
logger.warning(f"Source messages file not found: {source_file}")
|
||||||
return {
|
return {
|
||||||
@@ -129,14 +129,14 @@ def list_archives() -> List[Dict]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
# Pattern: {device_name}.YYYY-MM-DD.msgs
|
# Pattern: {device_name}.YYYY-MM-DD.msgs
|
||||||
pattern = f"{config.MC_DEVICE_NAME}.*.msgs"
|
pattern = f"{runtime_config.get_device_name()}.*.msgs"
|
||||||
|
|
||||||
for archive_file in archive_dir.glob(pattern):
|
for archive_file in archive_dir.glob(pattern):
|
||||||
try:
|
try:
|
||||||
# Extract date from filename
|
# Extract date from filename
|
||||||
# Format: DeviceName.YYYY-MM-DD.msgs
|
# Format: DeviceName.YYYY-MM-DD.msgs
|
||||||
filename = archive_file.name
|
filename = archive_file.name
|
||||||
date_part = filename.replace(f"{config.MC_DEVICE_NAME}.", "").replace(".msgs", "")
|
date_part = filename.replace(f"{runtime_config.get_device_name()}.", "").replace(".msgs", "")
|
||||||
|
|
||||||
# Validate date format
|
# Validate date format
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ Configuration module - loads settings from environment variables
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
@@ -51,3 +55,40 @@ class Config:
|
|||||||
|
|
||||||
# Global config instance
|
# Global config instance
|
||||||
config = Config()
|
config = Config()
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeConfig:
|
||||||
|
"""
|
||||||
|
Runtime configuration that can be updated after startup.
|
||||||
|
|
||||||
|
Used for values that are detected/fetched at runtime, like
|
||||||
|
device name from bridge auto-detection.
|
||||||
|
"""
|
||||||
|
_device_name: Optional[str] = None
|
||||||
|
_device_name_source: str = "config"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_device_name(cls, name: str, source: str = "detected"):
|
||||||
|
"""Set the runtime device name"""
|
||||||
|
cls._device_name = name
|
||||||
|
cls._device_name_source = source
|
||||||
|
logger.info(f"Runtime device name set: {name} (source: {source})")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_device_name(cls) -> str:
|
||||||
|
"""Get device name - prefers runtime value, falls back to config"""
|
||||||
|
return cls._device_name or config.MC_DEVICE_NAME
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_device_name_source(cls) -> str:
|
||||||
|
"""Get the source of the device name (detected/config/fallback)"""
|
||||||
|
return cls._device_name_source
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_msgs_file_path(cls) -> Path:
|
||||||
|
"""Get the full path to the .msgs file using runtime device name"""
|
||||||
|
return Path(config.MC_CONFIG_DIR) / f"{cls.get_device_name()}.msgs"
|
||||||
|
|
||||||
|
|
||||||
|
# Global runtime config instance
|
||||||
|
runtime_config = RuntimeConfig()
|
||||||
|
|||||||
+10
-1
@@ -5,13 +5,15 @@ mc-webui - Flask application entry point
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
|
import threading
|
||||||
import requests
|
import requests
|
||||||
from flask import Flask, request as flask_request
|
from flask import Flask, request as flask_request
|
||||||
from flask_socketio import SocketIO, emit
|
from flask_socketio import SocketIO, emit
|
||||||
from app.config import config
|
from app.config import config, runtime_config
|
||||||
from app.routes.views import views_bp
|
from app.routes.views import views_bp
|
||||||
from app.routes.api import api_bp
|
from app.routes.api import api_bp
|
||||||
from app.archiver.manager import schedule_daily_archiving
|
from app.archiver.manager import schedule_daily_archiving
|
||||||
|
from app.meshcore.cli import fetch_device_name_from_bridge
|
||||||
|
|
||||||
# Commands that require longer timeout (in seconds)
|
# Commands that require longer timeout (in seconds)
|
||||||
SLOW_COMMANDS = {
|
SLOW_COMMANDS = {
|
||||||
@@ -57,6 +59,13 @@ def create_app():
|
|||||||
else:
|
else:
|
||||||
logger.info("Archive scheduler disabled")
|
logger.info("Archive scheduler disabled")
|
||||||
|
|
||||||
|
# Fetch device name from bridge in background thread
|
||||||
|
def init_device_name():
|
||||||
|
device_name, source = fetch_device_name_from_bridge()
|
||||||
|
runtime_config.set_device_name(device_name, source)
|
||||||
|
|
||||||
|
threading.Thread(target=init_device_name, daemon=True).start()
|
||||||
|
|
||||||
logger.info(f"mc-webui started - device: {config.MC_DEVICE_NAME}")
|
logger.info(f"mc-webui started - device: {config.MC_DEVICE_NAME}")
|
||||||
logger.info(f"Messages file: {config.msgs_file_path}")
|
logger.info(f"Messages file: {config.msgs_file_path}")
|
||||||
logger.info(f"Serial port: {config.MC_SERIAL_PORT}")
|
logger.info(f"Serial port: {config.MC_SERIAL_PORT}")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ MeshCore CLI wrapper - executes meshcli commands via HTTP bridge
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
import requests
|
import requests
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Tuple, Optional, List, Dict
|
from typing import Tuple, Optional, List, Dict
|
||||||
@@ -965,3 +966,50 @@ def set_manual_add_contacts(enabled: bool) -> Tuple[bool, str]:
|
|||||||
return False, 'Cannot connect to meshcore-bridge service'
|
return False, 'Cannot connect to meshcore-bridge service'
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Device Name Detection
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
def fetch_device_name_from_bridge(max_retries: int = 3, retry_delay: float = 2.0) -> Tuple[Optional[str], str]:
|
||||||
|
"""
|
||||||
|
Fetch detected device name from meshcore-bridge /health endpoint.
|
||||||
|
|
||||||
|
The bridge auto-detects device name from meshcli prompt ("DeviceName|*")
|
||||||
|
and exposes it via /health endpoint.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
max_retries: Number of retry attempts if bridge is unavailable
|
||||||
|
retry_delay: Delay between retries in seconds
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (device_name, source)
|
||||||
|
- device_name: Detected name or fallback from config
|
||||||
|
- source: "detected", "config", or "fallback"
|
||||||
|
"""
|
||||||
|
bridge_health_url = config.MC_BRIDGE_URL.replace('/cli', '/health')
|
||||||
|
|
||||||
|
for attempt in range(max_retries):
|
||||||
|
try:
|
||||||
|
response = requests.get(bridge_health_url, timeout=5)
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
if data.get('status') == 'healthy':
|
||||||
|
device_name = data.get('device_name')
|
||||||
|
source = data.get('device_name_source', 'unknown')
|
||||||
|
if device_name:
|
||||||
|
logger.info(f"Got device name from bridge: {device_name} (source: {source})")
|
||||||
|
return device_name, source
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
logger.warning(f"Bridge not reachable, attempt {attempt + 1}/{max_retries}")
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
logger.warning(f"Bridge timeout, attempt {attempt + 1}/{max_retries}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Attempt {attempt + 1}/{max_retries} failed: {e}")
|
||||||
|
|
||||||
|
if attempt < max_retries - 1:
|
||||||
|
time.sleep(retry_delay)
|
||||||
|
|
||||||
|
logger.warning(f"Using fallback device name: {config.MC_DEVICE_NAME}")
|
||||||
|
return config.MC_DEVICE_NAME, "fallback"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict, Optional, Tuple
|
from typing import List, Dict, Optional, Tuple
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from app.config import config
|
from app.config import config, runtime_config
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ def parse_message(line: Dict, allowed_channels: Optional[List[int]] = None) -> O
|
|||||||
# Extract sender name
|
# Extract sender name
|
||||||
if is_own:
|
if is_own:
|
||||||
# For sent messages, use 'sender' field (meshcore-cli 1.3.12+)
|
# For sent messages, use 'sender' field (meshcore-cli 1.3.12+)
|
||||||
sender = line.get('sender', config.MC_DEVICE_NAME)
|
sender = line.get('sender', runtime_config.get_device_name())
|
||||||
content = text
|
content = text
|
||||||
else:
|
else:
|
||||||
# For received messages, extract sender from "SenderName: message" format
|
# For received messages, extract sender from "SenderName: message" format
|
||||||
@@ -91,7 +91,7 @@ def read_messages(limit: Optional[int] = None, offset: int = 0, archive_date: Op
|
|||||||
if archive_date:
|
if archive_date:
|
||||||
return read_archive_messages(archive_date, limit, offset, channel_idx)
|
return read_archive_messages(archive_date, limit, offset, channel_idx)
|
||||||
|
|
||||||
msgs_file = config.msgs_file_path
|
msgs_file = runtime_config.get_msgs_file_path()
|
||||||
|
|
||||||
if not msgs_file.exists():
|
if not msgs_file.exists():
|
||||||
logger.warning(f"Messages file not found: {msgs_file}")
|
logger.warning(f"Messages file not found: {msgs_file}")
|
||||||
@@ -268,7 +268,7 @@ def delete_channel_messages(channel_idx: int) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
True if successful, False otherwise
|
True if successful, False otherwise
|
||||||
"""
|
"""
|
||||||
msgs_file = config.msgs_file_path
|
msgs_file = runtime_config.get_msgs_file_path()
|
||||||
|
|
||||||
if not msgs_file.exists():
|
if not msgs_file.exists():
|
||||||
logger.warning(f"Messages file not found: {msgs_file}")
|
logger.warning(f"Messages file not found: {msgs_file}")
|
||||||
@@ -338,7 +338,7 @@ def _cleanup_old_dm_sent_log() -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dm_log_file = Path(config.MC_CONFIG_DIR) / f"{config.MC_DEVICE_NAME}_dm_sent.jsonl"
|
dm_log_file = Path(config.MC_CONFIG_DIR) / f"{runtime_config.get_device_name()}_dm_sent.jsonl"
|
||||||
if dm_log_file.exists():
|
if dm_log_file.exists():
|
||||||
dm_log_file.unlink()
|
dm_log_file.unlink()
|
||||||
logger.info(f"Cleaned up old DM sent log: {dm_log_file}")
|
logger.info(f"Cleaned up old DM sent log: {dm_log_file}")
|
||||||
@@ -420,7 +420,7 @@ def _parse_sent_msg(line: Dict) -> Optional[Dict]:
|
|||||||
timestamp = line.get('timestamp', 0)
|
timestamp = line.get('timestamp', 0)
|
||||||
# Use 'recipient' field (added in meshcore-cli 1.3.12), fallback to 'name'
|
# Use 'recipient' field (added in meshcore-cli 1.3.12), fallback to 'name'
|
||||||
recipient = line.get('recipient', line.get('name', 'Unknown'))
|
recipient = line.get('recipient', line.get('name', 'Unknown'))
|
||||||
sender = line.get('sender', config.MC_DEVICE_NAME)
|
sender = line.get('sender', runtime_config.get_device_name())
|
||||||
|
|
||||||
# Generate conversation ID from recipient name
|
# Generate conversation ID from recipient name
|
||||||
conversation_id = f"name_{recipient}"
|
conversation_id = f"name_{recipient}"
|
||||||
@@ -471,7 +471,7 @@ def read_dm_messages(
|
|||||||
_cleanup_old_dm_sent_log()
|
_cleanup_old_dm_sent_log()
|
||||||
|
|
||||||
# --- Read DM messages from .msgs file ---
|
# --- Read DM messages from .msgs file ---
|
||||||
msgs_file = config.msgs_file_path
|
msgs_file = runtime_config.get_msgs_file_path()
|
||||||
if msgs_file.exists():
|
if msgs_file.exists():
|
||||||
try:
|
try:
|
||||||
with open(msgs_file, 'r', encoding='utf-8') as f:
|
with open(msgs_file, 'r', encoding='utf-8') as f:
|
||||||
|
|||||||
+3
-2
@@ -11,7 +11,7 @@ from datetime import datetime
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from flask import Blueprint, jsonify, request, send_file
|
from flask import Blueprint, jsonify, request, send_file
|
||||||
from app.meshcore import cli, parser
|
from app.meshcore import cli, parser
|
||||||
from app.config import config
|
from app.config import config, runtime_config
|
||||||
from app.archiver import manager as archive_manager
|
from app.archiver import manager as archive_manager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -209,7 +209,8 @@ def get_status():
|
|||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'connected': connected,
|
'connected': connected,
|
||||||
'device_name': config.MC_DEVICE_NAME,
|
'device_name': runtime_config.get_device_name(),
|
||||||
|
'device_name_source': runtime_config.get_device_name_source(),
|
||||||
'serial_port': config.MC_SERIAL_PORT,
|
'serial_port': config.MC_SERIAL_PORT,
|
||||||
'message_count': message_count,
|
'message_count': message_count,
|
||||||
'latest_message_timestamp': latest_timestamp
|
'latest_message_timestamp': latest_timestamp
|
||||||
|
|||||||
+7
-7
@@ -4,7 +4,7 @@ HTML views for mc-webui
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from flask import Blueprint, render_template, request
|
from flask import Blueprint, render_template, request
|
||||||
from app.config import config
|
from app.config import config, runtime_config
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ def index():
|
|||||||
"""
|
"""
|
||||||
return render_template(
|
return render_template(
|
||||||
'index.html',
|
'index.html',
|
||||||
device_name=config.MC_DEVICE_NAME
|
device_name=runtime_config.get_device_name()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ def direct_messages():
|
|||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'dm.html',
|
'dm.html',
|
||||||
device_name=config.MC_DEVICE_NAME,
|
device_name=runtime_config.get_device_name(),
|
||||||
initial_conversation=initial_conversation
|
initial_conversation=initial_conversation
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ def contact_management():
|
|||||||
"""
|
"""
|
||||||
return render_template(
|
return render_template(
|
||||||
'contacts-manage.html',
|
'contacts-manage.html',
|
||||||
device_name=config.MC_DEVICE_NAME
|
device_name=runtime_config.get_device_name()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ def contact_pending_list():
|
|||||||
"""
|
"""
|
||||||
return render_template(
|
return render_template(
|
||||||
'contacts-pending.html',
|
'contacts-pending.html',
|
||||||
device_name=config.MC_DEVICE_NAME
|
device_name=runtime_config.get_device_name()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ def contact_existing_list():
|
|||||||
"""
|
"""
|
||||||
return render_template(
|
return render_template(
|
||||||
'contacts-existing.html',
|
'contacts-existing.html',
|
||||||
device_name=config.MC_DEVICE_NAME
|
device_name=runtime_config.get_device_name()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ def console():
|
|||||||
"""
|
"""
|
||||||
return render_template(
|
return render_template(
|
||||||
'console.html',
|
'console.html',
|
||||||
device_name=config.MC_DEVICE_NAME
|
device_name=runtime_config.get_device_name()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import json
|
|||||||
import queue
|
import queue
|
||||||
import uuid
|
import uuid
|
||||||
import shlex
|
import shlex
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from flask import Flask, request, jsonify
|
from flask import Flask, request, jsonify
|
||||||
from flask_socketio import SocketIO, emit
|
from flask_socketio import SocketIO, emit
|
||||||
@@ -57,6 +58,10 @@ class MeshCLISession:
|
|||||||
self.config_dir = Path(config_dir)
|
self.config_dir = Path(config_dir)
|
||||||
self.device_name = device_name
|
self.device_name = device_name
|
||||||
|
|
||||||
|
# Auto-detected device name from meshcli prompt
|
||||||
|
self.detected_name = None
|
||||||
|
self.name_detection_done = threading.Event()
|
||||||
|
|
||||||
# Ensure config directory exists
|
# Ensure config directory exists
|
||||||
self.config_dir.mkdir(parents=True, exist_ok=True)
|
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||||
self.advert_log_path = self.config_dir / f"{device_name}.adverts.jsonl"
|
self.advert_log_path = self.config_dir / f"{device_name}.adverts.jsonl"
|
||||||
@@ -178,6 +183,37 @@ class MeshCLISession:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to apply session settings: {e}")
|
logger.error(f"Failed to apply session settings: {e}")
|
||||||
|
|
||||||
|
# Wait for device name detection from prompt, then fallback to .infos
|
||||||
|
if not self.name_detection_done.wait(timeout=1.0):
|
||||||
|
logger.info("Device name not detected from prompt, trying .infos command")
|
||||||
|
self._detect_name_from_infos()
|
||||||
|
|
||||||
|
def _detect_name_from_infos(self):
|
||||||
|
"""Fallback: detect device name via .infos command"""
|
||||||
|
if self.detected_name:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self.execute_command(['.infos'], timeout=5)
|
||||||
|
if result['success'] and result['stdout']:
|
||||||
|
# Try to parse JSON output from .infos
|
||||||
|
stdout = result['stdout'].strip()
|
||||||
|
# Find JSON object in output
|
||||||
|
for line in stdout.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith('{'):
|
||||||
|
try:
|
||||||
|
data = json.loads(line)
|
||||||
|
if 'name' in data:
|
||||||
|
self.detected_name = data['name']
|
||||||
|
logger.info(f"Detected device name from .infos: {self.detected_name}")
|
||||||
|
self.name_detection_done.set()
|
||||||
|
return
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to detect device name from .infos: {e}")
|
||||||
|
|
||||||
def _read_stdout(self):
|
def _read_stdout(self):
|
||||||
"""Thread: Read stdout line-by-line, parse adverts vs CLI responses"""
|
"""Thread: Read stdout line-by-line, parse adverts vs CLI responses"""
|
||||||
logger.info("stdout reader thread started")
|
logger.info("stdout reader thread started")
|
||||||
@@ -191,6 +227,14 @@ class MeshCLISession:
|
|||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Detect device name from meshcli prompt: "DeviceName|*" or "DeviceName|*[E]"
|
||||||
|
if not self.detected_name and '|*' in line:
|
||||||
|
prompt_match = re.match(r'^(.+?)\|\*', line)
|
||||||
|
if prompt_match:
|
||||||
|
self.detected_name = prompt_match.group(1).strip()
|
||||||
|
logger.info(f"Detected device name from prompt: {self.detected_name}")
|
||||||
|
self.name_detection_done.set()
|
||||||
|
|
||||||
# Try to parse as JSON advert
|
# Try to parse as JSON advert
|
||||||
if self._is_advert_json(line):
|
if self._is_advert_json(line):
|
||||||
self._log_advert(line)
|
self._log_advert(line)
|
||||||
@@ -540,13 +584,24 @@ meshcli_session = None
|
|||||||
|
|
||||||
@app.route('/health', methods=['GET'])
|
@app.route('/health', methods=['GET'])
|
||||||
def health():
|
def health():
|
||||||
"""Health check endpoint"""
|
"""Health check endpoint with device name detection info"""
|
||||||
session_status = "healthy" if meshcli_session and meshcli_session.process and meshcli_session.process.poll() is None else "unhealthy"
|
session_status = "healthy" if meshcli_session and meshcli_session.process and meshcli_session.process.poll() is None else "unhealthy"
|
||||||
|
|
||||||
|
# Determine device name and source
|
||||||
|
detected_name = meshcli_session.detected_name if meshcli_session else None
|
||||||
|
device_name = detected_name or MC_DEVICE_NAME
|
||||||
|
name_source = "detected" if detected_name else "config"
|
||||||
|
|
||||||
|
# Log warning if there's a mismatch between detected and configured names
|
||||||
|
if detected_name and detected_name != MC_DEVICE_NAME:
|
||||||
|
logger.warning(f"Device name mismatch: detected='{detected_name}', configured='{MC_DEVICE_NAME}'")
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': session_status,
|
'status': session_status,
|
||||||
'serial_port': MC_SERIAL_PORT,
|
'serial_port': MC_SERIAL_PORT,
|
||||||
'advert_log': str(meshcli_session.advert_log_path) if meshcli_session else None
|
'advert_log': str(meshcli_session.advert_log_path) if meshcli_session else None,
|
||||||
|
'device_name': device_name,
|
||||||
|
'device_name_source': name_source
|
||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user