Search code examples
swiftmacoscocoaappkit

How can I get IOBluetoothDevice's battery level, using Swift and AppKit (Xcode for MacOS)


I am using Xcode to develop a MacOS app, based on Cocoa & AppKit, written in Swift.

I am using IOBluetoothDevice objects throughout my app, and I want to be able to display the devices' battery levels, if accessible.

I expect that devices which battery levels are visible on the OS's Bluetooth settings (see image below), to be also accessible programmatically (e.g., AirPods, Magic Keyboard, etc.). However, I could not find this anywhere.

I have also thought about executing a terminal command and found this thread, but it did also not work.

Thanks

Bluetooth device battery level available to the OS


Solution

  • You can get the battery level of Bluetooth devices from the IORegistry with IOKit.

    This is a simple example to get the battery level for the Magic Trackpad 2

    import IOKit
    
    var serialPortIterator = io_iterator_t()
    var object : io_object_t
    let port: mach_port_t
    if #available(macOS 12.0, *) {
        port = kIOMainPortDefault // New name in macOS 12 and higher
    } else {
        port = kIOMasterPortDefault // Old name in macOS 11 and lower
    }
    let matchingDict : CFDictionary = IOServiceMatching("AppleDeviceManagementHIDEventService")
    let kernResult = IOServiceGetMatchingServices(port, matchingDict, &serialPortIterator)
    
    if KERN_SUCCESS == kernResult {
        repeat {
            object = IOIteratorNext(serialPortIterator)
            if object != 0, let percent = IORegistryEntryCreateCFProperty(object, "BatteryPercent" as CFString, kCFAllocatorDefault, 0).takeRetainedValue() as? Int {
                print(percent)
                break
            }
        } while object != 0
        IOObjectRelease(object)
    }    
    IOObjectRelease(serialPortIterator)
    

    For other devices you have to replace AppleDeviceManagementHIDEventService and Trackpad2 with the appropriate values. You can display the entire IORegistry in Terminal.app with ioreg -l.