175 lines
5.5 KiB
Objective-C
175 lines
5.5 KiB
Objective-C
#import "MMComponentThermalProvider.h"
|
|
|
|
@interface MMComponentThermalProvider () {
|
|
MMAppleSMCClient *_smcClient;
|
|
NSMutableArray<NSString *> *_discoveredKeys;
|
|
BOOL _probed;
|
|
}
|
|
@end
|
|
|
|
@implementation MMComponentThermalProvider
|
|
|
|
- (instancetype)init {
|
|
return [self initWithSMCClient:[MMAppleSMCClient sharedClient]];
|
|
}
|
|
|
|
- (instancetype)initWithSMCClient:(MMAppleSMCClient *)client {
|
|
self = [super init];
|
|
if (self) {
|
|
_smcClient = client;
|
|
_discoveredKeys = [NSMutableArray array];
|
|
[self probeSensors];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)probeSensors {
|
|
if (!_smcClient.isAvailable) return;
|
|
|
|
NSArray<NSString *> *candidates = @[
|
|
@"TPCD", @"TP0P", @"TP0D",
|
|
@"Th0H", @"Th1H", @"Th2H", @"Th3H",
|
|
@"TM0P", @"TM1P", @"TM2P", @"TM3P", @"TM0S", @"TM1S",
|
|
@"Tp0P", @"Tp1P", @"Tp2P", @"Tp0C",
|
|
@"TA0P", @"TA1P", @"TA0S", @"TA1S",
|
|
@"TTLD", @"TT1D", @"TB0T", @"TB1T"
|
|
];
|
|
|
|
[_discoveredKeys removeAllObjects];
|
|
for (NSString *key in candidates) {
|
|
NSNumber *val = [_smcClient readNumericValueForKey:key error:nil];
|
|
if (val && [val doubleValue] > 0.0 && [val doubleValue] < 125.0) {
|
|
[_discoveredKeys addObject:key];
|
|
}
|
|
}
|
|
|
|
_probed = YES;
|
|
}
|
|
|
|
- (MMTelemetryDomain)domain {
|
|
return MMTelemetryDomainThermal;
|
|
}
|
|
|
|
- (NSString *)providerIdentifier {
|
|
return @"com.i3omb.macmonitor.telemetry.thermal.components";
|
|
}
|
|
|
|
- (BOOL)isAvailable {
|
|
return _smcClient.isAvailable;
|
|
}
|
|
|
|
- (MMAppleSMCClient *)smcClient {
|
|
return _smcClient;
|
|
}
|
|
|
|
- (NSArray<NSString *> *)discoveredKeys {
|
|
return [_discoveredKeys copy];
|
|
}
|
|
|
|
+ (NSString *)categoryForKey:(NSString *)key {
|
|
if ([key hasPrefix:@"TP"]) return @"PCH / Chipset";
|
|
if ([key hasPrefix:@"Th"]) return @"Heatsink";
|
|
if ([key hasPrefix:@"TM"]) return @"Memory";
|
|
if ([key hasPrefix:@"Tp"]) return @"Power Supply";
|
|
if ([key hasPrefix:@"TA"]) return @"Ambient / Enclosure";
|
|
if ([key hasPrefix:@"TT"] || [key hasPrefix:@"TB"] || [key hasPrefix:@"TI"]) return @"Thunderbolt / IO";
|
|
return @"Other";
|
|
}
|
|
|
|
+ (NSString *)humanReadableNameForKey:(NSString *)key {
|
|
static NSDictionary<NSString *, NSString *> *names = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
names = @{
|
|
@"TPCD": @"Platform Controller Hub (Die)",
|
|
@"TP0P": @"Platform Controller Hub (Proximity)",
|
|
@"TP0D": @"Platform Controller Hub (Die Alternate)",
|
|
@"Th0H": @"Main Heatsink A",
|
|
@"Th1H": @"Main Heatsink B",
|
|
@"Th2H": @"Main Heatsink C",
|
|
@"Th3H": @"Main Heatsink D",
|
|
@"TM0P": @"Memory Bank 0 Proximity",
|
|
@"TM1P": @"Memory Bank 1 Proximity",
|
|
@"TM2P": @"Memory Bank 2 Proximity",
|
|
@"TM3P": @"Memory Bank 3 Proximity",
|
|
@"TM0S": @"Memory Module 0",
|
|
@"TM1S": @"Memory Module 1",
|
|
@"Tp0P": @"Power Supply Primary",
|
|
@"Tp1P": @"Power Supply Secondary",
|
|
@"Tp2P": @"Power Supply Tertiary",
|
|
@"Tp0C": @"Power Supply Core",
|
|
@"TA0P": @"Ambient Air Intake",
|
|
@"TA1P": @"Ambient Air Exhaust",
|
|
@"TA0S": @"Ambient Air Enclosure",
|
|
@"TA1S": @"Ambient Air Secondary",
|
|
@"TTLD": @"Thunderbolt Controller Die",
|
|
@"TT1D": @"Thunderbolt Controller Secondary",
|
|
@"TB0T": @"Thunderbolt Port 0",
|
|
@"TB1T": @"Thunderbolt Port 1"
|
|
};
|
|
});
|
|
|
|
NSString *match = names[key];
|
|
return match ?: key;
|
|
}
|
|
|
|
+ (NSDictionary<NSString *, id> *)calculateComponentMetricsFromDictionary:(NSDictionary<NSString *, NSNumber *> *)readings {
|
|
NSMutableArray<NSDictionary<NSString *, id> *> *components = [NSMutableArray arrayWithCapacity:readings.count];
|
|
NSMutableDictionary<NSString *, NSMutableArray<NSDictionary<NSString *, id> *> *> *categorized = [NSMutableDictionary dictionary];
|
|
|
|
NSArray<NSString *> *sortedKeys = [readings.allKeys sortedArrayUsingSelector:@selector(compare:)];
|
|
double peak = 0.0;
|
|
double sum = 0.0;
|
|
|
|
for (NSString *k in sortedKeys) {
|
|
double temp = [readings[k] doubleValue];
|
|
if (temp > peak) peak = temp;
|
|
sum += temp;
|
|
|
|
NSString *cat = [self categoryForKey:k];
|
|
NSString *name = [self humanReadableNameForKey:k];
|
|
|
|
NSDictionary<NSString *, id> *entry = @{
|
|
@"key": k,
|
|
@"name": name,
|
|
@"category": cat,
|
|
@"temperature": @(temp)
|
|
};
|
|
|
|
[components addObject:entry];
|
|
|
|
if (!categorized[cat]) {
|
|
categorized[cat] = [NSMutableArray array];
|
|
}
|
|
[categorized[cat] addObject:entry];
|
|
}
|
|
|
|
double avg = readings.count > 0 ? (sum / (double)readings.count) : 0.0;
|
|
|
|
return @{
|
|
@"components": components,
|
|
@"categorized": categorized,
|
|
@"sensorCount": @(readings.count),
|
|
@"peakTemperature": @(peak),
|
|
@"averageTemperature": @(avg)
|
|
};
|
|
}
|
|
|
|
- (nullable NSDictionary<NSString *, id> *)sampleTelemetryWithError:(NSError **)error {
|
|
if (!_probed && _smcClient.isAvailable) {
|
|
[self probeSensors];
|
|
}
|
|
|
|
NSMutableDictionary<NSString *, NSNumber *> *readings = [NSMutableDictionary dictionary];
|
|
for (NSString *key in _discoveredKeys) {
|
|
NSNumber *temp = [_smcClient readNumericValueForKey:key error:nil];
|
|
if (temp) {
|
|
readings[key] = temp;
|
|
}
|
|
}
|
|
|
|
return [MMComponentThermalProvider calculateComponentMetricsFromDictionary:readings];
|
|
}
|
|
|
|
@end
|