feat: RTL_433 protocol database import - iteration 1/5
- Expanded protocol database from 18 → 299 signatures (16.6x increase) - Imported 281 protocols from RTL_433 open-source database (286 total devices) - Created automated import script: scripts/import_rtl433_protocols.py - Generated rtl433_protocols_imported.py with timing/frequency/modulation data - Updated protocol_database.py to include RTL433_PROTOCOLS - All 26 tests passing Breakdown by category: - Weather: 116 protocols - Sensors: 36 protocols - TPMS: 25 protocols - Security: 23 protocols - Home Automation: 18 protocols - Other: 50+ protocols Frequency coverage: - 433.92 MHz: 248 protocols - 315.00 MHz: 32 protocols - 915.00 MHz: 1 protocol This provides comprehensive coverage of Sub-GHz IoT devices for accurate identification from raw RF captures.
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Client-Side Error Reporter
|
||||
*
|
||||
* Automatically captures and reports JavaScript errors during file uploads
|
||||
* to help diagnose issues during beta testing.
|
||||
*/
|
||||
|
||||
class ErrorReporter {
|
||||
constructor() {
|
||||
this.uploadId = null;
|
||||
this.errorQueue = [];
|
||||
this.reportEndpoint = '/api/v1/captures/report_client_error';
|
||||
|
||||
// Setup global error handler
|
||||
this.setupGlobalErrorHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current upload ID for error tracking
|
||||
*/
|
||||
setUploadId(uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
console.log(`[ErrorReporter] Tracking upload: ${uploadId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup global error handlers
|
||||
*/
|
||||
setupGlobalErrorHandler() {
|
||||
// Catch unhandled JavaScript errors
|
||||
window.addEventListener('error', (event) => {
|
||||
this.reportError({
|
||||
error_type: event.error?.name || 'Error',
|
||||
error_message: event.message,
|
||||
stack_trace: event.error?.stack,
|
||||
filename: event.filename,
|
||||
lineno: event.lineno,
|
||||
colno: event.colno,
|
||||
source: 'window.error'
|
||||
});
|
||||
});
|
||||
|
||||
// Catch unhandled promise rejections
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
this.reportError({
|
||||
error_type: 'UnhandledPromiseRejection',
|
||||
error_message: event.reason?.message || String(event.reason),
|
||||
stack_trace: event.reason?.stack,
|
||||
source: 'unhandledrejection'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Report error to server
|
||||
*/
|
||||
async reportError(errorData) {
|
||||
const errorReport = {
|
||||
upload_id: this.uploadId || 'no_upload_id',
|
||||
timestamp: new Date().toISOString(),
|
||||
...errorData,
|
||||
browser_info: this.getBrowserInfo(),
|
||||
page_url: window.location.href
|
||||
};
|
||||
|
||||
// Log to console
|
||||
console.error('[ErrorReporter] Reporting error:', errorReport);
|
||||
|
||||
// Queue error (in case of network issues)
|
||||
this.errorQueue.push(errorReport);
|
||||
|
||||
// Try to send immediately
|
||||
try {
|
||||
await this.sendErrorReport(errorReport);
|
||||
// Remove from queue if successful
|
||||
this.errorQueue = this.errorQueue.filter(e => e !== errorReport);
|
||||
} catch (e) {
|
||||
console.warn('[ErrorReporter] Failed to send error report:', e);
|
||||
// Will retry later
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send error report to server
|
||||
*/
|
||||
async sendErrorReport(errorReport) {
|
||||
const response = await fetch(this.reportEndpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(errorReport)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Report parse error specifically
|
||||
*/
|
||||
reportParseError(filename, error, fileContentSample = '') {
|
||||
this.reportError({
|
||||
error_type: 'SubFileParseError',
|
||||
error_message: error.message || String(error),
|
||||
stack_trace: error.stack,
|
||||
filename: filename,
|
||||
file_content_sample: fileContentSample.substring(0, 500),
|
||||
source: 'sub_parser'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Report upload error
|
||||
*/
|
||||
reportUploadError(filename, error, stage = 'unknown') {
|
||||
this.reportError({
|
||||
error_type: 'UploadError',
|
||||
error_message: error.message || String(error),
|
||||
stack_trace: error.stack,
|
||||
filename: filename,
|
||||
upload_stage: stage,
|
||||
source: 'upload_handler'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Report validation error
|
||||
*/
|
||||
reportValidationError(field, value, reason) {
|
||||
this.reportError({
|
||||
error_type: 'ValidationError',
|
||||
error_message: `${field} validation failed: ${reason}`,
|
||||
validation_field: field,
|
||||
validation_value: String(value),
|
||||
source: 'validation'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Report network error
|
||||
*/
|
||||
reportNetworkError(url, status, statusText) {
|
||||
this.reportError({
|
||||
error_type: 'NetworkError',
|
||||
error_message: `${status} ${statusText}`,
|
||||
request_url: url,
|
||||
http_status: status,
|
||||
source: 'network'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get browser information
|
||||
*/
|
||||
getBrowserInfo() {
|
||||
return {
|
||||
user_agent: navigator.userAgent,
|
||||
platform: navigator.platform,
|
||||
language: navigator.language,
|
||||
screen_width: screen.width,
|
||||
screen_height: screen.height,
|
||||
viewport_width: window.innerWidth,
|
||||
viewport_height: window.innerHeight,
|
||||
connection_type: navigator.connection?.effectiveType,
|
||||
device_memory: navigator.deviceMemory,
|
||||
hardware_concurrency: navigator.hardwareConcurrency,
|
||||
online: navigator.onLine
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush error queue (send all pending errors)
|
||||
*/
|
||||
async flushErrorQueue() {
|
||||
const pending = [...this.errorQueue];
|
||||
this.errorQueue = [];
|
||||
|
||||
for (const error of pending) {
|
||||
try {
|
||||
await this.sendErrorReport(error);
|
||||
} catch (e) {
|
||||
// If still fails, put back in queue
|
||||
this.errorQueue.push(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sent: pending.length - this.errorQueue.length,
|
||||
failed: this.errorQueue.length
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate upload ID
|
||||
*/
|
||||
generateUploadId() {
|
||||
const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '_').substring(0, 15);
|
||||
const random = Math.random().toString(36).substring(2, 10);
|
||||
return `upload_${timestamp}_${random}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Create global instance
|
||||
window.errorReporter = new ErrorReporter();
|
||||
|
||||
// Export for module use
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = ErrorReporter;
|
||||
}
|
||||
Reference in New Issue
Block a user