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
, andmac
and works on both.
Windows
has 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/table
library to create, style, and render the table. - Clearing the Screen: Ensures a clean display for each update.
Types
screenWriter
struct: Represents the component responsible for writing to the screen.wg
: WaitGroup for coordination.ptab
: Atable.Writer
instance from thego-pretty/table
library.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 ascreenWriter
and starts its execution loop. -
execute()
The main loop of thescreenWriter
:- Waits for signals on the
quit
channel to stop. - Waits for device data on the
readPath
channel. - Calls
Write
to 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
resolveCompanyIdent
andisFindMyDevice
to 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
CompanyIdentifiers
struct. - 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
true
if 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
scanner
struct: 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 ofdevContent
structs.ingestPath
: A channel for transmitting the discovered device data.devContent
struct:** 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 thereturnPath
channel.newScanner(...)
: A constructor to create a newscanner
object.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 thedevices
map.sortAndPass()
: Sorts the devices by ID and sends them on theingPath
.