Skip to content

Commit 1b1fc7e

Browse files
committed
feat: Display battery information of a Apple laptop
1 parent c4c10dc commit 1b1fc7e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+6393
-0
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,38 @@
11
# Alfred Battery
22

3+
Display battery information of your Apple laptop in Alfred
4+
5+
![][demo]
6+
7+
Alfred Battery shows:
8+
9+
- the current charging state
10+
- the remaining time until full or empty
11+
- the tempertature
12+
- the charging cycles
13+
- the health
14+
- as well as the serial number
15+
16+
Alfred Battery uses a a [built-in Swift][built-in-swift] programm to retive the battery information from the system.
17+
18+
## Dependencies
19+
20+
The Alfred Battery workflow dependes on a [python3.7+][python] installation. Install `python3` e.g. via. [Homebrew][homebrew] `brew install python`.
21+
22+
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]
23+
24+
## Licence, thanks
25+
26+
The workflow code, the bundeled [Alfred-PyWorkflow][alfred-pyworkflow] library as well as the graphics are all under the [MIT Licence][mit-licence].
27+
28+
The original idea is from [BenziAhamed][benzi-ahamed] and his [Battery workflow][benzi-ahamed-alfred-battery]
29+
30+
[alfred-pyworkflow]: https://github.com/harrtho/alfred-pyworkflow
31+
[benzi-ahamed-alfred-battery]: https://github.com/BenziAhamed/alfred-battery
32+
[benzi-ahamed]: https://github.com/BenziAhamed
33+
[built-in-swift]: src/BatteryInfo.swift
34+
[command-line-tutorial]: https://www.freecodecamp.org/news/install-xcode-command-line-tools/
35+
[demo]: demo.gif
36+
[homebrew]: https://brew.sh
37+
[mit-licence]: http://opensource.org/licenses/MIT
38+
[python]: https://www.python.org

demo.gif

400 KB
Loading

res/BatteryAge.afdesign

18.7 KB
Binary file not shown.

res/BatteryCharging.afdesign

51.3 KB
Binary file not shown.

res/BatteryClock.afdesign

25.4 KB
Binary file not shown.

res/BatteryCritical.afdesign

65 KB
Binary file not shown.

res/BatteryCycles.afdesign

93.7 KB
Binary file not shown.

res/BatteryError.afdesign

56.3 KB
Binary file not shown.

res/BatteryFull.afdesign

98.4 KB
Binary file not shown.

res/BatteryHealth.afdesign

106 KB
Binary file not shown.

res/BatteryIcon.afdesign

98.4 KB
Binary file not shown.

res/BatteryLow.afdesign

49.9 KB
Binary file not shown.

res/BatteryMedium.afdesign

56.6 KB
Binary file not shown.

res/BatteryPower.afdesign

90.1 KB
Binary file not shown.

res/BatterySerial.afdesign

31.7 KB
Binary file not shown.

res/BatteryTemp.afdesign

500 KB
Binary file not shown.

res/BatteryUpdate.afdesign

87.8 KB
Binary file not shown.

src/BatteryInfo.swift

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//
2+
// BarreryInfo.swift
3+
//
4+
// Copyright (c) 2023 Thomas Harr <[email protected]>
5+
//
6+
// MIT Licence. See http://opensource.org/licenses/MIT
7+
//
8+
// Created on 2023-01-03
9+
//
10+
11+
import IOKit
12+
import Foundation
13+
14+
15+
func convertJSONSerializable(rawValue: Any) -> Any {
16+
switch rawValue {
17+
case is NSNumber:
18+
return rawValue as! NSNumber
19+
case is NSString:
20+
return rawValue as! NSString
21+
case is NSArray:
22+
let rawArray = rawValue as! NSArray
23+
let arr = NSMutableArray()
24+
for rawEntry in rawArray {
25+
arr.add(convertJSONSerializable(rawValue: rawEntry))
26+
}
27+
return arr
28+
case is NSDictionary:
29+
let rawDictionary = rawValue as! NSDictionary
30+
let dict = NSMutableDictionary()
31+
for (rawDictKey, rawDictValue) in rawDictionary {
32+
dict[convertJSONSerializable(rawValue: rawDictKey)] = convertJSONSerializable(rawValue: rawDictValue)
33+
}
34+
return dict
35+
case is NSData:
36+
return (rawValue as! NSData).hexEncodedString()
37+
default:
38+
return NSString(format: "Unknown type: '%s'", String(describing: type(of: rawValue)))
39+
}
40+
}
41+
42+
43+
extension NSData {
44+
struct HexEncodingOptions: OptionSet {
45+
let rawValue: Int
46+
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
47+
}
48+
49+
func hexEncodedString(options: HexEncodingOptions = []) -> String {
50+
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
51+
return self.map { String(format: format, $0) }.joined()
52+
}
53+
}
54+
55+
56+
var serviceIterator: io_iterator_t = io_iterator_t()
57+
58+
var waittime = mach_timespec_t(tv_sec: 1,tv_nsec: 0)
59+
IOServiceWaitQuiet(kIOMainPortDefault, &waittime)
60+
61+
if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceNameMatching("AppleSmartBattery"), &serviceIterator) == kIOReturnSuccess) {
62+
IOIteratorReset(serviceIterator)
63+
64+
repeat {
65+
let service: io_service_t = IOIteratorNext(serviceIterator)
66+
// Check if there is a service
67+
guard service != 0 else {
68+
break
69+
}
70+
71+
var serviceProperties: Unmanaged<CFMutableDictionary>?
72+
IORegistryEntryCreateCFProperties(service, &serviceProperties, kCFAllocatorDefault, 0)
73+
let serviceEntries = NSDictionary(dictionary:(serviceProperties?.takeUnretainedValue())!)
74+
75+
do {
76+
let batteryData = try JSONSerialization.data(withJSONObject: convertJSONSerializable(rawValue: serviceEntries))
77+
print(NSString(data: batteryData, encoding: String.Encoding.utf8.rawValue)! as String)
78+
} catch {
79+
print(error)
80+
}
81+
82+
serviceProperties?.release()
83+
IOObjectRelease(service)
84+
85+
} while(IOIteratorIsValid(serviceIterator) == boolean_t(truncating: true))
86+
}

0 commit comments

Comments
 (0)