Skip to content

Commit

Permalink
feat: Display battery information of a Apple laptop
Browse files Browse the repository at this point in the history
  • Loading branch information
harrtho committed Jan 5, 2023
1 parent c4c10dc commit 1b1fc7e
Show file tree
Hide file tree
Showing 43 changed files with 6,393 additions and 0 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,38 @@
# Alfred Battery

Display battery information of your Apple laptop in Alfred

![][demo]

Alfred Battery shows:

- the current charging state
- the remaining time until full or empty
- the tempertature
- the charging cycles
- the health
- as well as the serial number

Alfred Battery uses a a [built-in Swift][built-in-swift] programm to retive the battery information from the system.

## Dependencies

The Alfred Battery workflow dependes on a [python3.7+][python] installation. Install `python3` e.g. via. [Homebrew][homebrew] `brew install python`.

Additionaly during the first execution and after an update of the workflow, the workflow compiles the `BatteryInfo` programm. This programm dependes on swift and the macOS `command line tools`. [freeCodeCamp tutorial][command-line-tutorial]

## Licence, thanks

The workflow code, the bundeled [Alfred-PyWorkflow][alfred-pyworkflow] library as well as the graphics are all under the [MIT Licence][mit-licence].

The original idea is from [BenziAhamed][benzi-ahamed] and his [Battery workflow][benzi-ahamed-alfred-battery]

[alfred-pyworkflow]: https://github.com/harrtho/alfred-pyworkflow
[benzi-ahamed-alfred-battery]: https://github.com/BenziAhamed/alfred-battery
[benzi-ahamed]: https://github.com/BenziAhamed
[built-in-swift]: src/BatteryInfo.swift
[command-line-tutorial]: https://www.freecodecamp.org/news/install-xcode-command-line-tools/
[demo]: demo.gif
[homebrew]: https://brew.sh
[mit-licence]: http://opensource.org/licenses/MIT
[python]: https://www.python.org
Binary file added demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/BatteryAge.afdesign
Binary file not shown.
Binary file added res/BatteryCharging.afdesign
Binary file not shown.
Binary file added res/BatteryClock.afdesign
Binary file not shown.
Binary file added res/BatteryCritical.afdesign
Binary file not shown.
Binary file added res/BatteryCycles.afdesign
Binary file not shown.
Binary file added res/BatteryError.afdesign
Binary file not shown.
Binary file added res/BatteryFull.afdesign
Binary file not shown.
Binary file added res/BatteryHealth.afdesign
Binary file not shown.
Binary file added res/BatteryIcon.afdesign
Binary file not shown.
Binary file added res/BatteryLow.afdesign
Binary file not shown.
Binary file added res/BatteryMedium.afdesign
Binary file not shown.
Binary file added res/BatteryPower.afdesign
Binary file not shown.
Binary file added res/BatterySerial.afdesign
Binary file not shown.
Binary file added res/BatteryTemp.afdesign
Binary file not shown.
Binary file added res/BatteryUpdate.afdesign
Binary file not shown.
86 changes: 86 additions & 0 deletions src/BatteryInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// BarreryInfo.swift
//
// Copyright (c) 2023 Thomas Harr <xDevThomas@gmail.com>
//
// MIT Licence. See http://opensource.org/licenses/MIT
//
// Created on 2023-01-03
//

import IOKit
import Foundation


func convertJSONSerializable(rawValue: Any) -> Any {
switch rawValue {
case is NSNumber:
return rawValue as! NSNumber
case is NSString:
return rawValue as! NSString
case is NSArray:
let rawArray = rawValue as! NSArray
let arr = NSMutableArray()
for rawEntry in rawArray {
arr.add(convertJSONSerializable(rawValue: rawEntry))
}
return arr
case is NSDictionary:
let rawDictionary = rawValue as! NSDictionary
let dict = NSMutableDictionary()
for (rawDictKey, rawDictValue) in rawDictionary {
dict[convertJSONSerializable(rawValue: rawDictKey)] = convertJSONSerializable(rawValue: rawDictValue)
}
return dict
case is NSData:
return (rawValue as! NSData).hexEncodedString()
default:
return NSString(format: "Unknown type: '%s'", String(describing: type(of: rawValue)))
}
}


extension NSData {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}

func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return self.map { String(format: format, $0) }.joined()
}
}


var serviceIterator: io_iterator_t = io_iterator_t()

var waittime = mach_timespec_t(tv_sec: 1,tv_nsec: 0)
IOServiceWaitQuiet(kIOMainPortDefault, &waittime)

if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceNameMatching("AppleSmartBattery"), &serviceIterator) == kIOReturnSuccess) {
IOIteratorReset(serviceIterator)

repeat {
let service: io_service_t = IOIteratorNext(serviceIterator)
// Check if there is a service
guard service != 0 else {
break
}

var serviceProperties: Unmanaged<CFMutableDictionary>?
IORegistryEntryCreateCFProperties(service, &serviceProperties, kCFAllocatorDefault, 0)
let serviceEntries = NSDictionary(dictionary:(serviceProperties?.takeUnretainedValue())!)

do {
let batteryData = try JSONSerialization.data(withJSONObject: convertJSONSerializable(rawValue: serviceEntries))
print(NSString(data: batteryData, encoding: String.Encoding.utf8.rawValue)! as String)
} catch {
print(error)
}

serviceProperties?.release()
IOObjectRelease(service)

} while(IOIteratorIsValid(serviceIterator) == boolean_t(truncating: true))
}
Loading

0 comments on commit 1b1fc7e

Please sign in to comment.