Kudos
- Adam Catley AirTag - Extremely useful Air tag breakdown with information compiled by Adam Catley
- bluetooth assigned-numbers lists - Bluetooth.com assigned numbers list, used for resolving Company UUIDs
Objective
FindMy Device Scanner: Shows you what Apple Find my devices have been in your vecinity for a length of time.
Application is tested on
Windows,Linux, andmacand works on both.
Windowshas an annoying flicker. I'm not sure what a good method of dealing with the screen buffering is.
Bluetooth device scanner in two routines
- scanner routine
- Detects and maintains a list of local Bluetooth devices and sorts and passes down an ingest path.
- screen-writer routine.
- Receives pre sorted list of devices on the ingest path, and prints them to a table.
Screenshot
Below is an AI generated breakdown of how this code works.
Purpose
Displays the output of the Bluetooth scanner in a neatly formatted table on the terminal screen. It does this by:
- Receiving device data: Reads the stream of scanned device data from a channel.
- Formatting the data: Prepares data rows for a table, including resolving company names and marking "Find My" devices.
- Table Management: Uses the
go-pretty/tablelibrary to create, style, and render the table. - Clearing the Screen: Ensures a clean display for each update.
Types
screenWriterstruct: Represents the component responsible for writing to the screen.wg: WaitGroup for coordination.ptab: Atable.Writerinstance from thego-pretty/tablelibrary.header: The table's header row.quit: Channel to signal stopping the writer.readPath: Channel from which it receives device data
Functions
-
newWriter(...)Constructor for creating ascreenWriter. Initializes the table writer and other settings. -
startWriter(...)Bootstraps the screen writer process. Creates ascreenWriterand starts its execution loop. -
execute()The main loop of thescreenWriter:- Waits for signals on the
quitchannel to stop. - Waits for device data on the
readPathchannel. - Calls
Writeto update the table when data arrives.
- Waits for signals on the
-
Write(devs []devContent)Handles the table updates:- Iterates over the received device data.
- Calls helper functions like
resolveCompanyIdentandisFindMyDeviceto process device information. - Appends new rows to the table.
- Clears the screen with
clearScreen(you'll need to implement this function). - Renders the updated table.
- Resets the table rows for the next update.
-
Helper Functions
-
resolveCompanyIdent(c *CorpIdentMap, t uint16) string:- Takes a pointer to the
CorpIdentMap(c) and a company identifier (t). - Looks up the identifier in the map.
- Returns the corresponding company name if found, or "Unknown" if not.
- Takes a pointer to the
-
ingestCorpDevices(loc string) CorpIdentMap:- Opens the YAML file at the given location (
company_identifiers.yaml). - Defines structs to model the YAML data.
- Uses a YAML decoder to read the file contents into the
CompanyIdentifiersstruct. - Iterates over the decoded data, populating the
CorpIdentMap. - Returns the filled
CorpIdentMap.
- Opens the YAML file at the given location (
-
isFindMyDevice(b map[uint16][]byte) bool:- Takes the device's manufacturer data (
b). - Iterates through the manufacturer data, checking if the first byte of any entry matches the "Find My" ID.
- Returns
trueif a match is found, otherwisefalse.
- Takes the device's manufacturer data (
-
getCompanyIdent(md manData) uint16:- Takes the device's manufacturer data (
md). - If the data isn't empty, it simply returns the first manufacturer ID found (assumes that's how the company ID is embedded).
- Takes the device's manufacturer data (
This code implements a Bluetooth Low Energy (BLE) device scanner. Its essential functions are:
- Scanning for BLE Devices: Discovers nearby BLE devices and gathers their data.
- Storing Device Information: Maintains a list of discovered devices along with information like their addresses, manufacturer data, and the time they were last seen.
- Transmitting Device Data: Regularly sends the collected device data downstream for further processing.
- Managing Old Devices: Removes devices from the list if they haven't been seen within a specified time threshold.
Package and Imports
main: The main package for the executable program.fmt: Standard input/output (I/O) formatting.log: Simple logging.reflect: Runtime type inspection.sort: Sorting.sync: Synchronization primitives (e.g., WaitGroup).time: Time-related functions.tinygo.org/x/bluetooth: Bluetooth library (assumed to be TinyGo-specific).
Constants
Defines time intervals and buffer sizes for the scanning and data processing:
scanRate: How often to start a new scan.scanBufferSize: Capacity of the channel receiving scan results.scanLength: How long each scan lasts.writeTime: How often to send the collected devices to the ingest path.trimTime: How often to remove old devices from the storage.oldestDevice: Maximum age for devices in storage.
Types
scannerstruct: Represents the BLE scanner object.wg: WaitGroup for coordination.adptr: Bluetooth adapter.devices: Map storing device data (Key: UUID, Value: map[UUID]devContent)count: Count of discovered devices.start: Timestamp of when the scan began.quit: Channel to signal stopping the scan.ingPath: Channel to send device data.
DevContentList: A sortable slice ofdevContentstructs.ingestPath: A channel for transmitting the discovered device data.devContentstruct:** Represents information about a single BLE deviceid: UUID of the device.manufacturerData: Raw manufacturer data.localName: Device's advertised name.companyIdent: Company identifier extracted from manufacturer data.lastSeen: Timestamp when the device was last observed.
Functions
scan(returnPath chan bluetooth.ScanResult): The core scanning function. Performs repeated scans and sends results on thereturnPathchannel.newScanner(...): A constructor to create a newscannerobject.startScan(): Starts the scanner's main loop (scanning, writing data, and trimming).startBleScanner(wg *sync.WaitGroup, ingPath ingestPath, q chan any): Bootstraps the scanner, sets up the Bluetooth adapter, and starts the scanning process.scanlog(s string): Simple logging function.TrimMap(): Removes stale device entries from thedevicesmap.sortAndPass(): Sorts the devices by ID and sends them on theingPath.