🍁
Swift: macのCPU温度を取得する(Apple Silicon編)
M1チップのMacでCPUの温度を取得する方法。(妥当っぽさは検証してみているが、本当にこれで正しい値なのかは謎)
まず、Objective-Cを使っていくのでBridging-Header.h
を用意
Bridging-Header.h
#include <Foundation/Foundation.h>
extern NSDictionary* thermalDictionary();
次にセンサー名と値のディクショナリを取得するためのObjective-Cのコードを用意
sensors.m
#include <Foundation/Foundation.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <IOKit/hidsystem/IOHIDServiceClient.h>
#include <IOKit/hidsystem/IOHIDEventSystemClient.h>
// IOHIDPageAppleVendor 0xFF00
// IOHIDUsageAppleVendorTemperatureSensor 0x05
// IOHIDEventTypeTemperature 0x0F
IOHIDEventSystemClientRef IOHIDEventSystemClientCreate(CFAllocatorRef);
void IOHIDEventSystemClientSetMatching(IOHIDEventSystemClientRef, CFDictionaryRef);
CFTypeRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t, int64_t, int64_t);
CFDictionaryRef IOHIDServiceClientCopyProperties(IOHIDServiceClientRef, CFArrayRef);
double IOHIDEventGetFloatValue(CFTypeRef, int64_t);
NSDictionary *thermalDictionary(void) {
int32_t page = 0xFF00;
int32_t usage = 0x05;
NSDictionary* filter = @{
@(kIOHIDPrimaryUsagePageKey): @(page),
@(kIOHIDPrimaryUsageKey): @(usage)
};
IOHIDEventSystemClientRef client = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
IOHIDEventSystemClientSetMatching(client, (__bridge CFDictionaryRef)filter);
NSArray *services = CFBridgingRelease(IOHIDEventSystemClientCopyServices(client));
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
for (id obj in services) {
IOHIDServiceClientRef service = (__bridge IOHIDServiceClientRef)obj;
CFTypeRef event = IOHIDServiceClientCopyEvent(service, 0x0F, 0, 0);
NSString *name = CFBridgingRelease(IOHIDServiceClientCopyProperty(service, CFSTR("Product")));
if (event != nil && event != nil) {
double value = IOHIDEventGetFloatValue(event, 0x0F << 0x10);
mutableDict[name] = @(value);
}
if (event != nil) {
CFRelease(event);
}
}
CFRelease(client);
return mutableDict;
}
最後にSwiftの方でCPUの平均値を取得してみる。
class CPUTemperatureARM {
let sensors = [
"eACC MTR Temp Sensor0",
"eACC MTR Temp Sensor3",
"pACC MTR Temp Sensor2",
"pACC MTR Temp Sensor3",
"pACC MTR Temp Sensor4",
"pACC MTR Temp Sensor5",
"pACC MTR Temp Sensor7",
"pACC MTR Temp Sensor8",
"pACC MTR Temp Sensor9"
]
func get() -> Double {
guard let dict = thermalDictionary() as? [String: Double] else { return 0 }
let temperatures = dict
.sorted { $0.key < $1.key }
.map { (key: $0.key, value: $0.value) }
let M1Temp = temperatures.filter { sensors.contains($0.key) }
if M1Temp.isEmpty {
let M1ProTemp = dict.filter { $0.key.lowercased().hasSuffix("tcal") == false }
if M1ProTemp.isEmpty {
return 0
}
return M1ProTemp.reduce(0) { $0 + $1.value } / Double(M1ProTemp.count)
} else {
return M1Temp.reduce(0) { $0 + $1.value } / Double(M1Temp.count)
}
}
}
let value = CPUTemperatureARM().get()
print(String(format: "CPU Temperature: %4.1f°C", value))
参考
- freedomtan/sensors:Objective-Cでセンサー値を取得する方法について
- andreivdev/coolm1:どのセンサー値の平均を取ればいいかの参考に
Discussion