Skip to content

Instantly share code, notes, and snippets.

@banjun
Created January 24, 2019 05:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save banjun/ad0289760f7fd6dac49bacac4e13ff4b to your computer and use it in GitHub Desktop.
Save banjun/ad0289760f7fd6dac49bacac4e13ff4b to your computer and use it in GitHub Desktop.
Capture iBeacon packets and show them in Touch Bar
#!/usr/bin/swift
import CoreBluetooth
import AppKit
class AppDelegate: NSResponder, CBCentralManagerDelegate, NSTouchBarDelegate, NSApplicationDelegate {
let manager: CBCentralManager
var advertises: [Advertise] = []
override init() {
manager = CBCentralManager(delegate: nil, queue: DispatchQueue.main)
super.init()
manager.delegate = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
struct Advertise: CustomStringConvertible {
enum ManufacturerData {
case iBeacon(UUID, UInt16, UInt16) // uuid, major, minor
case apple(UInt8, UInt8, Data) // Type, Subtype, remainder
case other(Data)
}
var manufacturerData: ManufacturerData
var name: String?
var time: Date
var description: String {
let df = DateFormatter()
df.locale = Locale.current
df.dateStyle = .short
df.timeStyle = .medium
let timeString = df.string(from: time)
switch manufacturerData {
case let .iBeacon(uuid, major, minor):
return "\(timeString) iBeacon(\(name ?? "unknown")) (\(uuid.uuidString), \(major), \(minor))"
case let .apple(type, subtype, data):
return "\(timeString) apple(type=\((type, subtype)))(\(name ?? "unknown")) (\((data as NSData).description))"
case let .other(data):
return "\(timeString) other(\(name ?? "unknown")) (\((data as NSData).description))"
}
}
}
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
NSLog("%@", "\(#function): \(central.state.rawValue)")
guard central.state == .poweredOn else { return }
manager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard let manufacturerData = advertisementData["kCBAdvDataManufacturerData"] as? Data else { return } // ignore non iBeacon
let adData: Advertise.ManufacturerData
if manufacturerData.starts(with: [0x4c, 0x00, 0x02, 0x15]) && manufacturerData.count == 4 + 16 + 2 + 2 + 1 {
let (uuid, major, minor) = manufacturerData.withUnsafeBytes { p in
(NSUUID(uuidBytes: p + 4) as UUID,
CFSwapInt16BigToHost((p + 20).withMemoryRebound(to: UInt16.self, capacity: 1) {$0.pointee}),
CFSwapInt16BigToHost((p + 22).withMemoryRebound(to: UInt16.self, capacity: 1) {$0.pointee}))
}
adData = .iBeacon(uuid, major, minor)
} else if manufacturerData.starts(with: [0x4c, 0x00]) {
let (type, subtype) = manufacturerData.withUnsafeBytes { (p: UnsafePointer<UInt8>) in
((p + 2).pointee, (p + 3).pointee)
}
adData = .apple(type, subtype, Data(manufacturerData.dropFirst(4)))
} else {
adData = .other(manufacturerData)
}
let advertise = Advertise(manufacturerData: adData, name: peripheral.name, time: Date())
print("\(advertise)")
fflush(__stdoutp)
if #available(OSX 10.12, *), case .iBeacon = advertise.manufacturerData {
touchbarLabel.stringValue = advertise.description
}
}
@available(OSX 10.12.2, *)
lazy var touchbar: NSTouchBar = {
let tb = NSTouchBar()
tb.delegate = self
tb.defaultItemIdentifiers = [NSTouchBarItem.Identifier(rawValue: "hoge")]
return tb
}()
@available(OSX 10.12.2, *)
override func makeTouchBar() -> NSTouchBar? {
return touchbar
}
@available(OSX 10.12, *)
lazy var touchbarLabel: NSTextField = NSTextField(labelWithString: "")
@available(OSX 10.12.2, *)
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = touchbarLabel
return item
}
}
let appDelegate = AppDelegate()
let app = NSApplication.shared
app.setActivationPolicy(.regular)
app.delegate = appDelegate
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment