b083890e96
Integrated comprehensive device attribution system into GigLez:
1. Simple Device Matcher (src/matcher/simple_matcher.py):
- Frequency-based device categorization (315/433/868/915 MHz)
- Protocol-specific identification (Princeton, EV1527, Oregon Scientific, etc.)
- Modulation + frequency matching (OOK/FSK/ASK)
- Confidence scoring (0.4-0.95 range)
- 50+ device types covered
2. API Integration (src/api/main_simple.py):
- Device matching in upload pipeline
- Added device fields: device_name, device_category, match_confidence, match_method, device_description
- Top 5 alternative matches stored per capture
- New endpoint: GET /api/v1/captures/{id} for detail view
3. Frontend Implementation:
- Detail modal with comprehensive device information
- Device identification section with confidence bars
- Alternative matches display
- Signal, location, and metadata sections
- Keyboard (ESC) and click-outside modal closing
4. UI Enhancements (static/css/main.css):
- Modal overlay with backdrop blur
- Animated modal slide-in
- Confidence visualization (green/yellow/red bars)
- Responsive detail grid layout
- Device match cards with categories
5. JavaScript Integration:
- detail-modal.js: Comprehensive detail view renderer
- Updated map.js and search.js to use detail modal
- Removed placeholder functions
6. Utilities:
- scripts/rematch_captures.py: Re-run matcher on existing data
- Successfully re-matched 20 existing captures
Device Categories Supported:
- Consumer RF (remotes, sensors)
- Automotive (key fobs, TPMS)
- Home Automation (garage/gate openers, blinds)
- Sensors (weather stations, temperature)
- Security (door/window sensors, alarms)
- IoT (smart meters, LoRa devices)
- Industrial (SCADA, telemetry, RFID)
Match Methods:
- Protocol matching (highest confidence: 0.7-0.95)
- Frequency matching (0.4-0.7)
- Modulation + frequency matching (0.5-0.7)
Frontend now displays:
- Device name and category on map markers
- Confidence percentage
- Detailed device information modal
- Alternative device matches
- Match method explanation
All existing captures successfully identified with 60-70% confidence.
88 lines
2.6 KiB
Python
88 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Re-match existing captures with device matcher
|
||
|
||
Runs the device matcher on all existing captures in the database
|
||
"""
|
||
|
||
import sys
|
||
import json
|
||
from pathlib import Path
|
||
|
||
# Add project root to path
|
||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||
|
||
from src.matcher.simple_matcher import get_matcher
|
||
|
||
|
||
def rematch_captures(storage_file):
|
||
"""Re-run device matching on existing captures"""
|
||
|
||
if not Path(storage_file).exists():
|
||
print(f"❌ Storage file not found: {storage_file}")
|
||
return 1
|
||
|
||
# Load existing captures
|
||
with open(storage_file, 'r') as f:
|
||
data = json.load(f)
|
||
|
||
captures = data.get('captures', [])
|
||
|
||
if not captures:
|
||
print("ℹ️ No captures to process")
|
||
return 0
|
||
|
||
print(f"🔄 Re-matching {len(captures)} captures...")
|
||
|
||
matcher = get_matcher()
|
||
updated_count = 0
|
||
|
||
for i, capture in enumerate(captures, 1):
|
||
frequency = capture.get('frequency', 0)
|
||
protocol = capture.get('protocol', 'RAW')
|
||
preset = capture.get('preset', 'Unknown')
|
||
|
||
# Skip if already has device data
|
||
if capture.get('device_name'):
|
||
continue
|
||
|
||
# Perform matching
|
||
matches = matcher.match(frequency, protocol, preset)
|
||
best_match = matches[0] if matches else None
|
||
|
||
if best_match:
|
||
# Update capture with device data
|
||
capture['device_name'] = best_match.device_name
|
||
capture['device_category'] = best_match.device_category
|
||
capture['match_confidence'] = best_match.confidence
|
||
capture['match_method'] = best_match.match_method
|
||
capture['device_description'] = best_match.description
|
||
capture['matched_devices'] = [
|
||
{
|
||
"device_name": m.device_name,
|
||
"category": m.device_category,
|
||
"confidence": m.confidence,
|
||
"method": m.match_method,
|
||
"description": m.description
|
||
}
|
||
for m in matches[:5]
|
||
]
|
||
updated_count += 1
|
||
|
||
print(f" [{i}/{len(captures)}] {capture['filename']}: {best_match.device_name} ({best_match.confidence:.0%})")
|
||
else:
|
||
print(f" [{i}/{len(captures)}] {capture['filename']}: No match found")
|
||
|
||
# Save updated data
|
||
with open(storage_file, 'w') as f:
|
||
json.dump(data, f, indent=2)
|
||
|
||
print(f"\n✅ Updated {updated_count}/{len(captures)} captures with device information")
|
||
|
||
return 0
|
||
|
||
|
||
if __name__ == '__main__':
|
||
STORAGE_FILE = Path(__file__).parent.parent / "data" / "captures_simple.json"
|
||
sys.exit(rematch_captures(STORAGE_FILE))
|