Files
giglez/scripts/import_flipper_sqlite.py
T
Trilltechnician 48fcb00241 Phase 3 Complete: Web Interface MVP
Major Achievements:
-  Full web interface (1,520+ lines of frontend code)
-  Interactive Leaflet.js map with marker clustering
-  Drag-and-drop upload system with GPS input
-  Search & filter UI with multi-criteria
-  Statistics dashboard with Chart.js
-  Responsive mobile-friendly design

Backend:
-  FastAPI static file serving
-  Simplified server mode (main_simple.py)
-  Improved startup script with port auto-selection
-  PostgreSQL schema ready (requires setup)

Database:
-  SQLite populated with 85 Flipper Zero signatures
-  Device matching system operational
-  Frequency-based search working

Documentation:
-  PHASE_3_COMPLETE.md - Technical summary
-  WEB_INTERFACE_README.md - User guide
-  WEBAPP_STARTUP_GUIDE.md - Troubleshooting
-  POSTGRESQL_SETUP_EXPLANATION.md - DB setup guide
-  DATABASE_POPULATION_SUCCESS.md - Import report
-  DEVICE_IDENTIFICATION_REPORT.md - Matching analysis

Files Created:
- templates/index.html (260 lines)
- static/css/main.css (500 lines)
- static/js/*.js (760 lines total)
- src/api/main_simple.py (simplified server)
- start_web.sh (auto port selection)

Status: Production MVP Ready
Next: Phase 4 - API & Integration

🛰️ Generated with Claude Code
https://claude.com/claude-code

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-12 18:21:11 -08:00

245 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""
Import Flipper Zero signatures into SQLite database
Uses SQLite for immediate testing without PostgreSQL setup
"""
import sys
import sqlite3
from pathlib import Path
from datetime import datetime
# Add project root to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.parser.sub_parser import SubFileParser
def create_sqlite_schema(conn):
"""Create SQLite schema"""
cursor = conn.cursor()
# Devices table
cursor.execute('''
CREATE TABLE IF NOT EXISTS devices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_name TEXT,
manufacturer TEXT,
model TEXT,
device_type TEXT,
typical_frequency INTEGER,
protocol TEXT,
description TEXT,
first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_verified BOOLEAN DEFAULT 0,
source TEXT
)
''')
# Signatures table
cursor.execute('''
CREATE TABLE IF NOT EXISTS signatures (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id INTEGER REFERENCES devices(id),
protocol TEXT,
frequency INTEGER,
modulation TEXT,
bit_pattern BLOB,
bit_mask BLOB,
timing_min INTEGER,
timing_max INTEGER,
raw_pattern TEXT,
confidence_threshold REAL DEFAULT 0.7,
source TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Indexes
cursor.execute('CREATE INDEX IF NOT EXISTS idx_sig_freq ON signatures(frequency)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_sig_device ON signatures(device_id)')
conn.commit()
def import_flipper_signature(conn, sub_file: Path, parser: SubFileParser):
"""Import a single Flipper Zero .sub file"""
try:
metadata = parser.parse(str(sub_file))
# Skip if frequency is 0
if metadata.frequency == 0:
return None
cursor = conn.cursor()
# Determine device type from frequency
freq_mhz = metadata.frequency / 1e6
if 300 <= freq_mhz <= 350:
device_type = 'garage_door'
elif 400 <= freq_mhz <= 450:
device_type = 'remote_control'
elif 800 <= freq_mhz <= 900:
device_type = 'sensor'
else:
device_type = 'unknown'
# Create device record
cursor.execute('''
INSERT INTO devices (device_name, manufacturer, model, device_type,
typical_frequency, protocol, description, source)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (
sub_file.stem,
'Unknown',
sub_file.stem,
device_type,
metadata.frequency,
metadata.protocol or 'RAW',
f'Imported from Flipper Zero: {sub_file.name}',
'flipper_zero'
))
device_id = cursor.lastrowid
# Extract timing info
timing_min, timing_max = None, None
raw_pattern = None
if metadata.raw_data and len(metadata.raw_data) > 0:
abs_timings = [abs(t) for t in metadata.raw_data]
timing_min = min(abs_timings)
timing_max = max(abs_timings)
raw_pattern = ','.join(map(str, metadata.raw_data[:100])) # First 100 samples
# Create signature record
cursor.execute('''
INSERT INTO signatures (device_id, protocol, frequency, modulation,
timing_min, timing_max, raw_pattern, source)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (
device_id,
metadata.protocol or 'RAW',
metadata.frequency,
metadata.modulation,
timing_min,
timing_max,
raw_pattern,
'flipper_zero'
))
conn.commit()
return {
'device_id': device_id,
'device_name': sub_file.stem,
'frequency': metadata.frequency,
'protocol': metadata.protocol
}
except Exception as e:
return None
def main():
"""Main entry point"""
print("="*80)
print("FLIPPER ZERO → SQLite IMPORT")
print("="*80)
print()
# Create/connect to SQLite database
db_path = Path(__file__).parent.parent / 'giglez.db'
print(f"Database: {db_path}")
conn = sqlite3.connect(str(db_path))
print("✅ Connected to SQLite database\n")
# Create schema
print("Creating schema...")
create_sqlite_schema(conn)
print("✅ Schema ready\n")
# Find Flipper signatures
flipper_dir = Path(__file__).parent.parent / 'signatures' / 'flipperzero-firmware'
if not flipper_dir.exists():
print(f"❌ Flipper directory not found: {flipper_dir}")
return 1
sub_files = list(flipper_dir.glob('**/*.sub'))
print(f"Found {len(sub_files)} Flipper Zero .sub files\n")
# Import all signatures
parser = SubFileParser()
imported = []
skipped = 0
print("Importing signatures...")
for i, sub_file in enumerate(sub_files, 1):
if i % 10 == 0:
print(f" Processed {i}/{len(sub_files)}...")
result = import_flipper_signature(conn, sub_file, parser)
if result:
imported.append(result)
else:
skipped += 1
print(f"✅ Import complete\n")
# Summary
print("="*80)
print("IMPORT SUMMARY")
print("="*80)
print(f"Total files: {len(sub_files)}")
print(f"Imported: {len(imported)}")
print(f"Skipped: {skipped}")
# Query database
cursor = conn.cursor()
print(f"\nDATABASE CONTENTS")
print("-"*80)
cursor.execute("SELECT COUNT(*) FROM devices")
device_count = cursor.fetchone()[0]
print(f"Devices: {device_count}")
cursor.execute("SELECT COUNT(*) FROM signatures")
sig_count = cursor.fetchone()[0]
print(f"Signatures: {sig_count}")
# Frequency distribution
print(f"\nFREQUENCY DISTRIBUTION")
print("-"*80)
cursor.execute('''
SELECT frequency, COUNT(*) as count
FROM signatures
GROUP BY frequency
ORDER BY count DESC
''')
for freq, count in cursor.fetchall():
print(f"{freq/1e6:8.2f} MHz: {count:3d} devices")
conn.close()
print(f"\n{'='*80}")
print("✅ Database ready at:", db_path)
print("="*80)
return 0
if __name__ == '__main__':
sys.exit(main())