-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
546 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,9 @@ | ||
# SMC | ||
# SMC | ||
### Wrapper for macOS's System Management Controller (SMC) written in Swift. | ||
|
||
#### License | ||
MIT license. See the LICENSE file for more information. | ||
|
||
#### References | ||
- [SMCWrapper](https://github.com/FergusInLondon/SMCWrapper) | ||
- [SMCKit](https://github.com/beltex/SMCKit) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// | ||
// Device.swift | ||
// smc | ||
// | ||
// Created by Daniel Storm on 7/17/19. | ||
// Copyright © 2019 Daniel Storm (github.com/DanielStormApps). | ||
// | ||
|
||
import Foundation | ||
|
||
struct Device { | ||
|
||
struct Controller { | ||
|
||
static let isT2: Bool = { | ||
let system_profiler = Process() | ||
let pipe = Pipe() | ||
|
||
system_profiler.launchPath = "/usr/sbin/system_profiler" | ||
system_profiler.arguments = ["SPiBridgeDataType"] | ||
system_profiler.standardOutput = pipe | ||
|
||
system_profiler.launch() | ||
system_profiler.waitUntilExit() | ||
|
||
let data = pipe.fileHandleForReading.readDataToEndOfFile() | ||
let output: String? = String(data: data, encoding: .utf8) | ||
return output?.contains("Apple T2 Security Chip") == true ? true : false | ||
}() | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// | ||
// SMC+CPU.swift | ||
// smc | ||
// | ||
// Created by Daniel Storm on 7/17/19. | ||
// Copyright © 2019 Daniel Storm (github.com/DanielStormApps). | ||
// | ||
|
||
import Foundation | ||
|
||
extension SMC { | ||
|
||
// MARK: - CPU | ||
public func cpuTemperature(key: SensorKey = Sensor.CPU.proximity) -> Temperature? { | ||
guard let bytes = bytes(key: key) else { return nil } | ||
|
||
let celsius = Double(bytes.0 & 0x7F) | ||
|
||
return Temperature(celsius: celsius, | ||
fahrenheit: Temperature.fahrenheit(celsius: celsius), | ||
kelvin: Temperature.kelvin(celsius: celsius)) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// | ||
// SMC+Extensions.swift | ||
// smc | ||
// | ||
// Created by Daniel Storm on 6/30/19. | ||
// Copyright © 2019 Daniel Storm (github.com/DanielStormApps). | ||
// | ||
|
||
import Foundation | ||
|
||
extension String { | ||
|
||
func smcKey() -> UInt32? { | ||
guard self.count == 4 else { return nil } | ||
|
||
let value = self.utf8.reduce(0) { sumOfBits, character in | ||
return sumOfBits << 8 | UInt32(character) | ||
} | ||
|
||
return value | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// | ||
// SMC+Fans.swift | ||
// smc | ||
// | ||
// Created by Daniel Storm on 7/17/19. | ||
// Copyright © 2019 Daniel Storm (github.com/DanielStormApps). | ||
// | ||
|
||
import Foundation | ||
|
||
extension SMC { | ||
|
||
// MARK: - Fans | ||
struct Fan { | ||
let identifier: Int | ||
let currentRPM: Int? | ||
let minimumRPM: Int? | ||
let maximumRPM: Int? | ||
let targetRPM: Int? | ||
} | ||
|
||
public func fans() -> [Fan] { | ||
guard let numberOfFans = numberOfFans() else { return [] } | ||
var fans: [Fan] = [] | ||
for i in 0..<numberOfFans { fans.append(fan(at: i)) } | ||
return fans | ||
} | ||
|
||
public func numberOfFans() -> Int? { | ||
guard let bytes = bytes(key: "FNum") else { return nil } | ||
return Int(bytes.0) | ||
} | ||
|
||
private func fan(at index: Int) -> Fan { | ||
return Fan(identifier: index, | ||
currentRPM: fanCurrentRPM(for: index), | ||
minimumRPM: fanMinimumRPM(for: index), | ||
maximumRPM: fanMaximumRPM(for: index), | ||
targetRPM: fanTargetRPM(for: index)) | ||
} | ||
|
||
private func fanCurrentRPM(for identifier: Int) -> Int? { | ||
guard let bytes = bytes(key: "F\(identifier)Ac") else { return nil } | ||
return integer(for: bytes) | ||
} | ||
|
||
private func fanMinimumRPM(for identifier: Int) -> Int? { | ||
guard let bytes = bytes(key: "F\(identifier)Mn") else { return nil } | ||
return integer(for: bytes) | ||
} | ||
|
||
private func fanMaximumRPM(for identifier: Int) -> Int? { | ||
guard let bytes = bytes(key: "F\(identifier)Mx") else { return nil } | ||
return integer(for: bytes) | ||
} | ||
|
||
private func fanTargetRPM(for identifier: Int) -> Int? { | ||
guard let bytes = bytes(key: "F\(identifier)Tg") else { return nil } | ||
return integer(for: bytes) | ||
} | ||
|
||
// MARK: - Helpers | ||
private func integer(for bytes: SMCBytes) -> Int { | ||
// Devices that have a T2 security chip use an FLT data type | ||
if Device.Controller.isT2 { | ||
var value: Float = 0.0 | ||
memcpy(&value, [bytes.0, bytes.1, bytes.2, bytes.3], 4) | ||
return Int(value) | ||
} | ||
else { | ||
return (Int(bytes.0) << 6) + (Int(bytes.1) >> 2) | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// | ||
// SMC+GPU.swift | ||
// smc | ||
// | ||
// Created by Daniel Storm on 7/17/19. | ||
// Copyright © 2019 Daniel Storm (github.com/DanielStormApps). | ||
// | ||
|
||
import Foundation | ||
|
||
extension SMC { | ||
|
||
// MARK: - GPU | ||
public func gpuTemperature(key: SensorKey = Sensor.GPU.proximity) -> Temperature? { | ||
guard let bytes = bytes(key: key) else { return nil } | ||
|
||
let celsius = Double(bytes.0 & 0x7F) | ||
|
||
return Temperature(celsius: celsius, | ||
fahrenheit: Temperature.fahrenheit(celsius: celsius), | ||
kelvin: Temperature.kelvin(celsius: celsius)) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// SMC+Sensor.swift | ||
// smc | ||
// | ||
// Created by Daniel Storm on 7/17/19. | ||
// Copyright © 2019 Daniel Storm (github.com/DanielStormApps). | ||
// | ||
|
||
import Foundation | ||
|
||
typealias SensorKey = String | ||
|
||
extension SMC { | ||
|
||
// MARK: - Sensor | ||
public struct Sensor { | ||
|
||
// MARK: - CPU | ||
public struct CPU { | ||
public static let proximity: SensorKey = "TC0P" | ||
} | ||
|
||
// MARK: - GPU | ||
public struct GPU { | ||
public static let proximity: SensorKey = "TG0P" | ||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// | ||
// SMC.swift | ||
// smc | ||
// | ||
// Created by Daniel Storm on 6/30/19. | ||
// Copyright © 2019 Daniel Storm (github.com/DanielStormApps). | ||
// | ||
|
||
import Foundation | ||
import IOKit | ||
|
||
class SMC { | ||
|
||
private static var connection: io_connect_t = 0 | ||
|
||
// MARK: Init | ||
static let shared = SMC() | ||
private init() { | ||
openConnection() | ||
} | ||
|
||
// MARK: - Connection Lifecycle | ||
private func openConnection() { | ||
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSMC")) | ||
assert(IOServiceOpen(service, mach_task_self_, 0, &SMC.connection) == kIOReturnSuccess, "Unable to start SMC") | ||
IOObjectRelease(service) | ||
} | ||
|
||
private func closeConnection() { | ||
IOServiceClose(SMC.connection) | ||
} | ||
|
||
// MARK: - SMC | ||
public func bytes(key: String) -> SMCBytes? { | ||
guard let smcKey = key.smcKey() else { return nil } | ||
let outputDataSize = dataSize(smcKey: smcKey) | ||
let outputBytes = bytes(smcKey: smcKey, dataSize: outputDataSize) | ||
return outputBytes | ||
} | ||
|
||
// MARK: - Helpers | ||
private func dataSize(smcKey: UInt32) -> IOByteCount { | ||
var inputStructure = SMCStructure() | ||
var outputStructure = SMCStructure() | ||
|
||
let inputStructureSize = MemoryLayout<SMCStructure>.stride | ||
var outputStructureSize = MemoryLayout<SMCStructure>.stride | ||
|
||
inputStructure.key = smcKey | ||
inputStructure.data8 = 9 | ||
|
||
let _ = IOConnectCallStructMethod(SMC.connection, | ||
2, | ||
&inputStructure, | ||
inputStructureSize, | ||
&outputStructure, | ||
&outputStructureSize) | ||
|
||
return outputStructure.keyInfo.dataSize | ||
} | ||
|
||
private func bytes(smcKey: UInt32, dataSize: UInt32) -> SMCBytes { | ||
var inputStructure = SMCStructure() | ||
var outputStructure = SMCStructure() | ||
|
||
let inputStructureSize = MemoryLayout<SMCStructure>.stride | ||
var outputStructureSize = MemoryLayout<SMCStructure>.stride | ||
|
||
inputStructure.key = smcKey | ||
inputStructure.keyInfo.dataSize = dataSize | ||
inputStructure.data8 = 5 | ||
|
||
let _ = IOConnectCallStructMethod(SMC.connection, | ||
2, | ||
&inputStructure, | ||
inputStructureSize, | ||
&outputStructure, | ||
&outputStructureSize) | ||
|
||
return outputStructure.bytes | ||
} | ||
|
||
// MARK: - Deinit | ||
deinit { | ||
closeConnection() | ||
} | ||
|
||
} | ||
|
||
extension SMC { | ||
|
||
#if DEBUG | ||
/// - Note: Only available in `DEBUG` environment. | ||
func printSystemInformation() { | ||
print("------------------") | ||
print("System Information") | ||
print("------------------") | ||
|
||
// Fans | ||
print() | ||
let fans = SMC.shared.fans() | ||
for fan in fans { | ||
print("Fan: \(fan)") | ||
} | ||
|
||
// CPU | ||
print() | ||
let cpuTemperature = SMC.shared.cpuTemperature() | ||
print("CPU C: \(String(describing: cpuTemperature?.celsius))") | ||
print("CPU F: \(String(describing: cpuTemperature?.fahrenheit))") | ||
print("CPU K: \(String(describing: cpuTemperature?.kelvin))") | ||
|
||
// GPU | ||
print() | ||
let gpuTemperature = SMC.shared.gpuTemperature() | ||
print("GPU C: \(String(describing: gpuTemperature?.celsius))") | ||
print("GPU F: \(String(describing: gpuTemperature?.fahrenheit))") | ||
print("GPU K: \(String(describing: gpuTemperature?.kelvin))") | ||
|
||
print() | ||
print("------------------") | ||
} | ||
#endif | ||
} |
Oops, something went wrong.