Initial commit: RF Mapper v0.3.0-dev
WiFi & Bluetooth signal mapping tool for Raspberry Pi with: - WiFi scanning via iw command - Bluetooth Classic/BLE device discovery - RSSI-based distance estimation - OUI manufacturer lookup - Web dashboard with multiple views: - Radar view (polar plot) - 2D Map (Leaflet/OpenStreetMap) - 3D Map (MapLibre GL JS with building extrusion) - Floor-based device positioning - Live BT tracking mode (auto-starts on page load) - SQLite database for historical device tracking: - RSSI time-series history - Device statistics (avg/min/max) - Movement detection and velocity estimation - Activity patterns (hourly/daily) - New device alerts - Automatic data retention/cleanup - REST API for all functionality Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
126
src/rf_mapper/bluetooth_class.py
Normal file
126
src/rf_mapper/bluetooth_class.py
Normal file
@@ -0,0 +1,126 @@
|
||||
"""Bluetooth Class of Device decoder"""
|
||||
|
||||
|
||||
class BluetoothClassDecoder:
|
||||
"""Decode Bluetooth Class of Device (CoD) codes"""
|
||||
|
||||
MAJOR_DEVICE_CLASSES = {
|
||||
0: "Miscellaneous",
|
||||
1: "Computer",
|
||||
2: "Phone",
|
||||
3: "LAN/Network",
|
||||
4: "Audio/Video",
|
||||
5: "Peripheral",
|
||||
6: "Imaging",
|
||||
7: "Wearable",
|
||||
8: "Toy",
|
||||
9: "Health",
|
||||
31: "Uncategorized"
|
||||
}
|
||||
|
||||
MINOR_COMPUTER = {
|
||||
0: "Uncategorized",
|
||||
1: "Desktop",
|
||||
2: "Server",
|
||||
3: "Laptop",
|
||||
4: "Handheld/PDA",
|
||||
5: "Palm/PDA",
|
||||
6: "Wearable"
|
||||
}
|
||||
|
||||
MINOR_PHONE = {
|
||||
0: "Uncategorized",
|
||||
1: "Cellular",
|
||||
2: "Cordless",
|
||||
3: "Smartphone",
|
||||
4: "Modem/Gateway",
|
||||
5: "ISDN"
|
||||
}
|
||||
|
||||
MINOR_AV = {
|
||||
0: "Uncategorized",
|
||||
1: "Wearable Headset",
|
||||
2: "Hands-free",
|
||||
4: "Microphone",
|
||||
5: "Loudspeaker",
|
||||
6: "Headphones",
|
||||
7: "Portable Audio",
|
||||
8: "Car Audio",
|
||||
9: "Set-top Box",
|
||||
10: "HiFi Audio",
|
||||
11: "VCR",
|
||||
12: "Video Camera",
|
||||
13: "Camcorder",
|
||||
14: "Video Monitor",
|
||||
15: "Video Display and Loudspeaker",
|
||||
16: "Video Conferencing",
|
||||
18: "Gaming/Toy"
|
||||
}
|
||||
|
||||
MINOR_PERIPHERAL = {
|
||||
0: "Uncategorized",
|
||||
1: "Keyboard",
|
||||
2: "Pointing Device",
|
||||
3: "Combo Keyboard/Pointing"
|
||||
}
|
||||
|
||||
MINOR_IMAGING = {
|
||||
1: "Display",
|
||||
2: "Camera",
|
||||
4: "Scanner",
|
||||
8: "Printer"
|
||||
}
|
||||
|
||||
MINOR_WEARABLE = {
|
||||
1: "Wristwatch",
|
||||
2: "Pager",
|
||||
3: "Jacket",
|
||||
4: "Helmet",
|
||||
5: "Glasses"
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def decode(cls, class_hex: str) -> tuple[str, str]:
|
||||
"""
|
||||
Decode Class of Device hex string into device type and category.
|
||||
|
||||
Args:
|
||||
class_hex: Hex string of Class of Device (e.g., "0x240404")
|
||||
|
||||
Returns:
|
||||
Tuple of (major_class, minor_class)
|
||||
"""
|
||||
try:
|
||||
cod = int(class_hex, 16)
|
||||
major = (cod >> 8) & 0x1F
|
||||
minor = (cod >> 2) & 0x3F
|
||||
|
||||
major_str = cls.MAJOR_DEVICE_CLASSES.get(major, f"Unknown ({major})")
|
||||
|
||||
minor_str = ""
|
||||
if major == 1:
|
||||
minor_str = cls.MINOR_COMPUTER.get(minor, f"Unknown ({minor})")
|
||||
elif major == 2:
|
||||
minor_str = cls.MINOR_PHONE.get(minor, f"Unknown ({minor})")
|
||||
elif major == 4:
|
||||
minor_str = cls.MINOR_AV.get(minor, f"Unknown ({minor})")
|
||||
elif major == 5:
|
||||
minor_str = cls.MINOR_PERIPHERAL.get(minor & 0x03, f"Unknown ({minor})")
|
||||
elif major == 6:
|
||||
minor_str = cls.MINOR_IMAGING.get(minor & 0x0F, f"Unknown ({minor})")
|
||||
elif major == 7:
|
||||
minor_str = cls.MINOR_WEARABLE.get(minor, f"Unknown ({minor})")
|
||||
else:
|
||||
minor_str = str(minor) if minor else ""
|
||||
|
||||
return major_str, minor_str
|
||||
except (ValueError, TypeError):
|
||||
return "Unknown", ""
|
||||
|
||||
@classmethod
|
||||
def decode_to_string(cls, class_hex: str) -> str:
|
||||
"""Decode to a single descriptive string"""
|
||||
major, minor = cls.decode(class_hex)
|
||||
if minor:
|
||||
return f"{major} ({minor})"
|
||||
return major
|
||||
Reference in New Issue
Block a user