mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
272 lines
8.4 KiB
Swift
272 lines
8.4 KiB
Swift
//
|
|
// readers.swift
|
|
// Sensors
|
|
//
|
|
// Created by Serhiy Mytrovtsiy on 17/06/2020.
|
|
// Using Swift 5.0.
|
|
// Running on macOS 10.15.
|
|
//
|
|
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
import Kit
|
|
import IOKit.hid
|
|
|
|
internal class SensorsReader: Reader<[Sensor_p]> {
|
|
internal var list: [Sensor_p] = []
|
|
}
|
|
|
|
internal class x86_SensorsReader: SensorsReader {
|
|
init() {
|
|
super.init()
|
|
|
|
var available: [String] = SMC.shared.getAllKeys()
|
|
var list: [Sensor] = []
|
|
|
|
if let count = SMC.shared.getValue("FNum") {
|
|
debug("Found \(Int(count)) fans", log: self.log)
|
|
|
|
for i in 0..<Int(count) {
|
|
self.list.append(Fan(
|
|
id: i,
|
|
key: "F\(i)Ac",
|
|
name: SMC.shared.getStringValue("F\(i)ID") ?? "Fan #\(i)",
|
|
minSpeed: SMC.shared.getValue("F\(i)Mn") ?? 1,
|
|
maxSpeed: SMC.shared.getValue("F\(i)Mx") ?? 1,
|
|
value: SMC.shared.getValue("F\(i)Ac") ?? 0,
|
|
mode: self.getFanMode(i)
|
|
))
|
|
}
|
|
}
|
|
|
|
available = available.filter({ (key: String) -> Bool in
|
|
switch key.prefix(1) {
|
|
case "T", "V", "P": return true
|
|
default: return false
|
|
}
|
|
})
|
|
|
|
SensorsList.forEach { (s: Sensor) in
|
|
if let idx = available.firstIndex(where: { $0 == s.key }) {
|
|
list.append(s)
|
|
available.remove(at: idx)
|
|
}
|
|
}
|
|
|
|
SensorsList.filter{ $0.key.contains("%") }.forEach { (s: Sensor) in
|
|
var index = 1
|
|
for i in 0..<10 {
|
|
let key = s.key.replacingOccurrences(of: "%", with: "\(i)")
|
|
if available.firstIndex(where: { $0 == key }) != nil {
|
|
var sensor = s.copy()
|
|
sensor.key = key
|
|
sensor.name = s.name.replacingOccurrences(of: "%", with: "\(index)")
|
|
|
|
list.append(sensor)
|
|
index += 1
|
|
}
|
|
}
|
|
}
|
|
|
|
for sensor in list {
|
|
if let newValue = SMC.shared.getValue(sensor.key) {
|
|
if let idx = list.firstIndex(where: { $0.key == sensor.key }) {
|
|
list[idx].value = newValue
|
|
}
|
|
}
|
|
}
|
|
|
|
self.list += list.filter({ (s: Sensor) -> Bool in
|
|
if s.type == .temperature && s.value > 110 {
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
public override func read() {
|
|
for i in 0..<self.list.count {
|
|
if let newValue = SMC.shared.getValue(self.list[i].key) {
|
|
self.list[i].value = newValue
|
|
}
|
|
}
|
|
self.callback(self.list)
|
|
}
|
|
|
|
private func getFanMode(_ id: Int) -> FanMode {
|
|
let fansMode: Int = Int(SMC.shared.getValue("FS! ") ?? 0)
|
|
var mode: FanMode = .automatic
|
|
|
|
if fansMode == 0 {
|
|
mode = .automatic
|
|
} else if fansMode == 3 {
|
|
mode = .forced
|
|
} else if fansMode == 1 && id == 0 {
|
|
mode = .forced
|
|
} else if fansMode == 2 && id == 1 {
|
|
mode = .forced
|
|
}
|
|
|
|
return mode
|
|
}
|
|
}
|
|
|
|
internal class AppleSilicon_SensorsReader: SensorsReader {
|
|
private let types: [SensorType] = [.temperature, .current, .voltage]
|
|
|
|
init() {
|
|
super.init()
|
|
|
|
for type in types {
|
|
self.fetch(type: type)
|
|
}
|
|
|
|
self.list = self.list.filter({ (s: Sensor_p) -> Bool in
|
|
switch s.type {
|
|
case .temperature:
|
|
return s.value < 110 && s.value >= 0
|
|
case .voltage:
|
|
return s.value < 300 && s.value >= 0
|
|
case .current:
|
|
return s.value < 100 && s.value >= 0
|
|
default: return true
|
|
}
|
|
}).sorted { $0.key.lowercased() < $1.key.lowercased() }
|
|
|
|
self.calculateAverage()
|
|
}
|
|
|
|
public override func read() {
|
|
for type in types {
|
|
self.fetch(type: type)
|
|
}
|
|
|
|
self.calculateAverage()
|
|
self.callback(self.list)
|
|
}
|
|
|
|
private func fetch(type: SensorType) {
|
|
var page: Int32 = 0
|
|
var usage: Int32 = 0
|
|
var eventType: Int32 = kIOHIDEventTypeTemperature
|
|
|
|
// usagePage:
|
|
// kHIDPage_AppleVendor = 0xff00,
|
|
// kHIDPage_AppleVendorTemperatureSensor = 0xff05,
|
|
// kHIDPage_AppleVendorPowerSensor = 0xff08,
|
|
// kHIDPage_GenericDesktop
|
|
//
|
|
// usage:
|
|
// kHIDUsage_AppleVendor_TemperatureSensor = 0x0005,
|
|
// kHIDUsage_AppleVendorPowerSensor_Current = 0x0002,
|
|
// kHIDUsage_AppleVendorPowerSensor_Voltage = 0x0003,
|
|
// kHIDUsage_GD_Keyboard
|
|
//
|
|
|
|
switch type {
|
|
case .temperature:
|
|
page = 0xff00
|
|
usage = 0x0005
|
|
eventType = kIOHIDEventTypeTemperature
|
|
case .current:
|
|
page = 0xff08
|
|
usage = 0x0003
|
|
eventType = kIOHIDEventTypePower
|
|
case .voltage:
|
|
page = 0xff08
|
|
usage = 0x0002
|
|
eventType = kIOHIDEventTypePower
|
|
case .power: break
|
|
case .fan: break
|
|
}
|
|
|
|
if let list = AppleSiliconSensors(page, usage, eventType) {
|
|
list.forEach { (key, value) in
|
|
if let name = key as? String, let value = value as? Double {
|
|
self.upsert(key: name, value: value, type: type)
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
private func createDeviceMatchingDictionary(usagePage: Int, usage: Int) -> CFMutableDictionary {
|
|
let dict = [
|
|
kIOHIDPrimaryUsageKey: usage,
|
|
kIOHIDPrimaryUsagePageKey: usagePage
|
|
] as NSDictionary
|
|
|
|
return dict.mutableCopy() as! NSMutableDictionary
|
|
}
|
|
|
|
private func upsert(key: String, value: Double, type: SensorType, group: SensorGroup = .system, prepend: Bool = false) {
|
|
if let idx = self.list.firstIndex(where: { $0.name == key }) {
|
|
self.list[idx].value = value
|
|
} else {
|
|
var name: String = key
|
|
var g: SensorGroup = group
|
|
|
|
AppleSiliconSensorsList.filter{ $0.key.contains("%") }.forEach { (s: Sensor) in
|
|
var index = 1
|
|
for i in 0..<64 {
|
|
if s.key.replacingOccurrences(of: "%", with: "\(i)") == key {
|
|
name = s.name.replacingOccurrences(of: "%", with: "\(index)")
|
|
}
|
|
index += 1
|
|
}
|
|
g = s.group
|
|
}
|
|
|
|
let s = Sensor(
|
|
key: key,
|
|
name: name,
|
|
value: value,
|
|
group: g,
|
|
type: type
|
|
)
|
|
|
|
if prepend {
|
|
self.list.insert(s, at: 0)
|
|
} else {
|
|
self.list.append(s)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func calculateAverage() {
|
|
let cpuSensors = self.list.filter({ $0.key.hasPrefix("pACC MTR Temp") || $0.key.hasPrefix("eACC MTR Temp") }).map{ $0.value }
|
|
let gpuSensors = self.list.filter({ $0.key.hasPrefix("GPU MTR Temp") }).map{ $0.value }
|
|
let socSensors = self.list.filter({ $0.key.hasPrefix("SOC MTR Temp") }).map{ $0.value }
|
|
|
|
if !socSensors.isEmpty {
|
|
self.upsert(
|
|
key: "Average SOC",
|
|
value: socSensors.reduce(0, +) / Double(socSensors.count),
|
|
type: .temperature,
|
|
group: .system,
|
|
prepend: true
|
|
)
|
|
}
|
|
if !gpuSensors.isEmpty {
|
|
self.upsert(
|
|
key: "Average GPU",
|
|
value: gpuSensors.reduce(0, +) / Double(gpuSensors.count),
|
|
type: .temperature,
|
|
group: .GPU,
|
|
prepend: true
|
|
)
|
|
}
|
|
if !cpuSensors.isEmpty {
|
|
self.upsert(
|
|
key: "Average CPU",
|
|
value: cpuSensors.reduce(0, +) / Double(cpuSensors.count),
|
|
type: .temperature,
|
|
group: .CPU,
|
|
prepend: true
|
|
)
|
|
}
|
|
}
|
|
}
|