From 46b8ff782d0e7a0fc26c205ee8facde08953a4e2 Mon Sep 17 00:00:00 2001 From: Tom Underhill Date: Sat, 27 Jul 2024 22:49:24 +0200 Subject: [PATCH 1/2] Add Fan sensors --- Hot.xcodeproj/project.pbxproj | 24 ++++++++ Hot/Classes/ApplicationDelegate.swift | 50 +++++++++++++++ Hot/Classes/FanSpeedToString.swift | 64 +++++++++++++++++++ Hot/Classes/FanViewController.swift | 46 ++++++++++++++ Hot/Classes/GraphWindowController.swift | 1 + Hot/Classes/InfoViewController.swift | 8 +++ Hot/Classes/ThermalLog.swift | 60 +++++++++++++----- Hot/UI/Base.lproj/FanViewController.xib | 74 ++++++++++++++++++++++ Hot/UI/Base.lproj/InfoViewController.xib | 67 +++++++++++++++++--- Hot/UI/Base.lproj/MainMenu.xib | 20 +++++- Hot/UI/GraphWindowController.xib | 75 +++++++++++++++++++---- Hot/UI/Images/FanTemplate.pdf | Bin 0 -> 3325 bytes 12 files changed, 449 insertions(+), 40 deletions(-) create mode 100644 Hot/Classes/FanSpeedToString.swift create mode 100644 Hot/Classes/FanViewController.swift create mode 100644 Hot/UI/Base.lproj/FanViewController.xib create mode 100644 Hot/UI/Images/FanTemplate.pdf diff --git a/Hot.xcodeproj/project.pbxproj b/Hot.xcodeproj/project.pbxproj index 283d30b..70af8c9 100644 --- a/Hot.xcodeproj/project.pbxproj +++ b/Hot.xcodeproj/project.pbxproj @@ -51,6 +51,10 @@ 05B0DFAA267150F40068FF9B /* TCXCTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 05B0DFA8267150F40068FF9B /* TCXCTemplate.pdf */; }; 05BA0BAA26F342DA00AA7BFC /* PressureToString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05BA0BA926F342DA00AA7BFC /* PressureToString.swift */; }; 05BA0BAD26F347A500AA7BFC /* PressureTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 05BA0BAC26F347A500AA7BFC /* PressureTemplate.pdf */; }; + 382CF88F2C4FF77400D9DB3C /* FanSpeedToString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382CF88E2C4FF77400D9DB3C /* FanSpeedToString.swift */; }; + 388C8F752C5522FC0057EA05 /* FanViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 388C8F732C5522FC0057EA05 /* FanViewController.xib */; }; + 388C8F772C5523260057EA05 /* FanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388C8F762C5523260057EA05 /* FanViewController.swift */; }; + 38B72A8D2C53DAFF00AD0B87 /* FanTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 38B72A8A2C53DAFF00AD0B87 /* FanTemplate.pdf */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -193,6 +197,10 @@ 05B0DFA8267150F40068FF9B /* TCXCTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = TCXCTemplate.pdf; sourceTree = ""; }; 05BA0BA926F342DA00AA7BFC /* PressureToString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PressureToString.swift; sourceTree = ""; }; 05BA0BAC26F347A500AA7BFC /* PressureTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = PressureTemplate.pdf; sourceTree = ""; }; + 382CF88E2C4FF77400D9DB3C /* FanSpeedToString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FanSpeedToString.swift; sourceTree = ""; }; + 388C8F742C5522FC0057EA05 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/FanViewController.xib; sourceTree = ""; }; + 388C8F762C5523260057EA05 /* FanViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FanViewController.swift; sourceTree = ""; }; + 38B72A8A2C53DAFF00AD0B87 /* FanTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = FanTemplate.pdf; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -227,6 +235,7 @@ 057F8AFF251D22360048F1E1 /* StatusIconTemplate.pdf */, 05BA0BAC26F347A500AA7BFC /* PressureTemplate.pdf */, 052FBCD2251F842C00C4322E /* TemperatureTemplate.pdf */, + 38B72A8A2C53DAFF00AD0B87 /* FanTemplate.pdf */, ); path = Images; sourceTree = ""; @@ -414,6 +423,7 @@ 057F8B0B251D273E0048F1E1 /* AboutWindowController.swift */, 05AA4079251CF3D200106CEA /* ApplicationDelegate.swift */, 05221E87267009A8007246FF /* SensorViewController.swift */, + 388C8F762C5523260057EA05 /* FanViewController.swift */, 055A932A252780FD0070F771 /* GraphView.swift */, 057F8B0E251D274C0048F1E1 /* Hot-Bridging-Header.h */, 055A931F252778C10070F771 /* InfoViewController.swift */, @@ -428,6 +438,7 @@ 05AA408C251CFA0600106CEA /* ThermalLog.swift */, 05AD458729084FEF00A791C8 /* SelectSensorsWindowController.swift */, 05566A74299195F400E7A5F1 /* GraphWindowController.swift */, + 382CF88E2C4FF77400D9DB3C /* FanSpeedToString.swift */, ); path = Classes; sourceTree = ""; @@ -441,6 +452,7 @@ 05AA407D251CF3D600106CEA /* MainMenu.xib */, 052FBCA5251EE46700C4322E /* PreferencesWindowController.xib */, 05221E8926700A57007246FF /* SensorViewController.xib */, + 388C8F732C5522FC0057EA05 /* FanViewController.xib */, 05AD458E2908505900A791C8 /* SelectSensorsWindowController.xib */, 05566A772991963400E7A5F1 /* GraphWindowController.xib */, ); @@ -544,12 +556,14 @@ files = ( 052FBCD4251F842C00C4322E /* SpeedTemplate.pdf in Resources */, 05BA0BAD26F347A500AA7BFC /* PressureTemplate.pdf in Resources */, + 388C8F752C5522FC0057EA05 /* FanViewController.xib in Resources */, 052FBCD6251F842C00C4322E /* TemperatureTemplate.pdf in Resources */, 057F8B00251D22360048F1E1 /* StatusIconTemplate.pdf in Resources */, 057F8B1B251D2A970048F1E1 /* Icon.icns in Resources */, 052FBCD5251F842C00C4322E /* CPUTemplate.pdf in Resources */, 05221E8B26700A57007246FF /* SensorViewController.xib in Resources */, 05AD459229092CEB00A791C8 /* TCalTemplate.pdf in Resources */, + 38B72A8D2C53DAFF00AD0B87 /* FanTemplate.pdf in Resources */, 05AD458C2908505900A791C8 /* SelectSensorsWindowController.xib in Resources */, 055A9323252778D00070F771 /* InfoViewController.xib in Resources */, 05B0DFA526714A830068FF9B /* UnknownTemplate.pdf in Resources */, @@ -594,6 +608,8 @@ buildActionMask = 2147483647; files = ( 055A932525277E9E0070F771 /* IntegerToString.swift in Sources */, + 388C8F772C5523260057EA05 /* FanViewController.swift in Sources */, + 382CF88F2C4FF77400D9DB3C /* FanSpeedToString.swift in Sources */, 055A932725277F6E0070F771 /* TemperatureToString.swift in Sources */, 05221E8626700772007246FF /* DictionaryIsEmpty.swift in Sources */, 052FBCA3251EE43B00C4322E /* PreferencesWindowController.swift in Sources */, @@ -670,6 +686,14 @@ name = SelectSensorsWindowController.xib; sourceTree = ""; }; + 388C8F732C5522FC0057EA05 /* FanViewController.xib */ = { + isa = PBXVariantGroup; + children = ( + 388C8F742C5522FC0057EA05 /* Base */, + ); + name = FanViewController.xib; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/Hot/Classes/ApplicationDelegate.swift b/Hot/Classes/ApplicationDelegate.swift index 5407c93..0b3a363 100644 --- a/Hot/Classes/ApplicationDelegate.swift +++ b/Hot/Classes/ApplicationDelegate.swift @@ -35,11 +35,13 @@ class ApplicationDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate private var sensorsWindowController: SensorsWindowController? private var selectSensorsWindowController: SelectSensorsWindowController? private var sensorViewControllers: [ SensorViewController ] = [] + private var fanViewControllers: [ FanViewController ] = [] private var graphWindowController: GraphWindowController? private var exiting = false @IBOutlet private var menu: NSMenu! @IBOutlet private var sensorsMenu: NSMenu! + @IBOutlet private var fansMenu: NSMenu! @IBOutlet private var updater: GitHubUpdater! @objc public private( set ) dynamic var infoViewController: InfoViewController? @@ -82,6 +84,7 @@ class ApplicationDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate self?.graphWindowController?.availableCPUs = self?.infoViewController?.availableCPUs ?? 0 self?.graphWindowController?.speedLimit = self?.infoViewController?.speedLimit ?? 0 self?.graphWindowController?.temperature = self?.infoViewController?.temperature ?? 0 + self?.graphWindowController?.fanSpeed = self?.infoViewController?.fanSpeed ?? 0 self?.graphWindowController?.thermalPressure = self?.infoViewController?.thermalPressure ?? 0 } @@ -357,18 +360,30 @@ class ApplicationDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { self.sensorsMenu.removeAllItems() } + if self.fanViewControllers.isEmpty + { + self.fansMenu.removeAllItems() + } guard let sensors = self.infoViewController?.log.sensors else { return } + guard let fans = self.infoViewController?.log.fans + else + { + return + } var controllers = self.sensorViewControllers + var fanControllers = self.fanViewControllers var items = self.sensorsMenu.items + var fanItems = self.fansMenu.items controllers.removeAll { item in sensors.contains { $0.key == item.name } == false } items.removeAll { item in sensors.contains { $0.key == item.title } == false } + fanItems.removeAll { item in fans.contains { $0.key == item.title } == false } sensors.forEach { @@ -403,6 +418,41 @@ class ApplicationDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { self.sensorsMenu.addItem( $0 ) } + + fans.forEach + { + fan in + + if let controller = self.fanViewControllers.first( where: { $0.name == fan.key } ) + { + controller.value = Int( fan.value ) + } + else + { + let controller = FanViewController() + controller.name = fan.key + controller.value = Int( fan.value ) + let item = NSMenuItem( title: fan.key, action: nil, keyEquivalent: "" ) + item.view = controller.view + + fanItems.append( item ) + fanControllers.append( controller ) + } + } + + self.fanViewControllers = fanControllers + + self.fansMenu.removeAllItems() + + + fanItems.sorted + { + $0.title.compare( $1.title, options: [ .numeric, .caseInsensitive ], range: nil, locale: nil ) == .orderedAscending + } + .forEach + { + self.fansMenu.addItem( $0 ) + } } @IBAction diff --git a/Hot/Classes/FanSpeedToString.swift b/Hot/Classes/FanSpeedToString.swift new file mode 100644 index 0000000..7e6b837 --- /dev/null +++ b/Hot/Classes/FanSpeedToString.swift @@ -0,0 +1,64 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2022, Jean-David Gadina - www.xs-labs.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +import Cocoa + +@objc( FanSpeedToString ) +public class FanSpeedToString: ValueTransformer +{ + public override class func transformedValueClass() -> AnyClass + { + return NSString.self + } + + public override class func allowsReverseTransformation() -> Bool + { + return false + } + + public override func transformedValue( _ value: Any? ) -> Any? + { + let n: Int? = + { + if let n = value as? Int + { + return n + } + else if let n = value as? Double + { + return Int( n ) + } + + return nil + }() + + guard let n = n, n > 0 + else + { + return "Off" as NSString + } + + return "\( n ) RPM" as NSString + } +} diff --git a/Hot/Classes/FanViewController.swift b/Hot/Classes/FanViewController.swift new file mode 100644 index 0000000..c10db06 --- /dev/null +++ b/Hot/Classes/FanViewController.swift @@ -0,0 +1,46 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2022, Jean-David Gadina - www.xs-labs.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the Software), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +import Cocoa + +public class FanViewController: NSViewController +{ + @objc private dynamic var icon = NSImage( named: "Unknown" ) + @objc private dynamic var label = "Unknown:" + @objc public dynamic var value = 0 + @objc public dynamic var name = "Unknown" + { + didSet + { + self.label = self.name.hasSuffix( ":" ) ? self.name : "\( self.name ):" + + self.icon = NSImage( named: "FanTemplate" ) + } + } + + public override var nibName: NSNib.Name? + { + "FanViewController" + } +} diff --git a/Hot/Classes/GraphWindowController.swift b/Hot/Classes/GraphWindowController.swift index 9932b63..f96d65c 100644 --- a/Hot/Classes/GraphWindowController.swift +++ b/Hot/Classes/GraphWindowController.swift @@ -33,6 +33,7 @@ public class GraphWindowController: NSWindowController @objc public dynamic var availableCPUs: Int = 0 @objc public dynamic var speedLimit: Int = 0 @objc public dynamic var temperature: Int = 0 + @objc public dynamic var fanSpeed: Int = 0 @objc public dynamic var thermalPressure: Int = 0 @objc public dynamic var showOnAllSpaces: Bool = false { diff --git a/Hot/Classes/InfoViewController.swift b/Hot/Classes/InfoViewController.swift index 22f1944..41a29e5 100644 --- a/Hot/Classes/InfoViewController.swift +++ b/Hot/Classes/InfoViewController.swift @@ -33,8 +33,10 @@ public class InfoViewController: NSViewController @objc public private( set ) dynamic var availableCPUs: Int = 0 @objc public private( set ) dynamic var speedLimit: Int = 0 @objc public private( set ) dynamic var temperature: Int = 0 + @objc public private( set ) dynamic var fanSpeed: Int = 0 @objc public private( set ) dynamic var thermalPressure: Int = 0 @objc public private( set ) dynamic var hasSensors: Bool = false + @objc public private( set ) dynamic var hasFans: Bool = false public var onUpdate: ( () -> Void )? @@ -117,6 +119,7 @@ public class InfoViewController: NSViewController private func update() { self.hasSensors = self.log.sensors.isEmpty == false + self.hasFans = self.log.fans.isEmpty == false if let n = self.log.schedulerLimit?.intValue { @@ -138,6 +141,11 @@ public class InfoViewController: NSViewController self.temperature = n } + if let n = self.log.fanSpeed?.intValue + { + self.fanSpeed = n + } + if let n = self.log.thermalPressure?.intValue { self.thermalPressure = n diff --git a/Hot/Classes/ThermalLog.swift b/Hot/Classes/ThermalLog.swift index 06cb820..ab37c61 100644 --- a/Hot/Classes/ThermalLog.swift +++ b/Hot/Classes/ThermalLog.swift @@ -32,19 +32,24 @@ public class ThermalLog: NSObject @objc public dynamic var availableCPUs: NSNumber? @objc public dynamic var speedLimit: NSNumber? @objc public dynamic var temperature: NSNumber? + @objc public dynamic var fanSpeed: NSNumber? @objc public dynamic var thermalPressure: NSNumber? @objc public dynamic var sensors: [ String: Double ] = [ : ] + @objc public dynamic var fans: [ String: Double ] = [ : ] private var refreshing = false private static var queue = DispatchQueue( label: "com.xs-labs.Hot.ThermalLog", qos: .background, attributes: [], autoreleaseFrequency: .workItem, target: nil ) + private var regexFanRPM: NSRegularExpression + public override init() { + self.regexFanRPM = try! NSRegularExpression( pattern: "F[0-9]Ac" ) super.init() } - private func readSensors() -> [ String: ( temperature: Double, isCPU: Bool ) ] + private func readSensors() -> [ String: ( value: Double, isCPU: Bool, isFan: Bool ) ] { let ioHID = IOHID.shared.readTemperatureSensors().compactMap { @@ -53,29 +58,26 @@ public class ThermalLog: NSObject let smc = SMC.shared.readAllKeys { - $0 >> 24 == 84 // T prefix (four char code) + $0 >> 24 == 84 || // T prefix (four char code) + $0 >> 24 == 70 // F prefix } .compactMap { self.sensorValue( data: $0 ) } - let all = [ ioHID, smc ].flatMap { $0 }.filter - { - $0.1.temperature > 0 && $0.1.temperature < 120 - } - + let all = [ ioHID, smc ].flatMap { $0 } return Dictionary( uniqueKeysWithValues: all ) } - private func sensorValue( data: IOHIDData ) -> ( String, ( temperature: Double, isCPU: Bool ) )? + private func sensorValue( data: IOHIDData ) -> ( String, ( value: Double, isCPU: Bool, isFan: Bool ) )? { let isCPU = data.name.hasPrefix( "pACC" ) || data.name.hasPrefix( "eACC" ) - return ( data.name, ( temperature: data.value, isCPU: isCPU ) ) + return ( data.name, ( value: data.value, isCPU: isCPU, isFan: false ) ) } - private func sensorValue( data: SMCData ) -> ( String, ( temperature: Double, isCPU: Bool ) )? + private func sensorValue( data: SMCData ) -> ( String, ( value: Double, isCPU: Bool, isFan: Bool ) )? { let value: Double @@ -92,7 +94,7 @@ public class ThermalLog: NSObject return nil } - return ( data.keyName, ( temperature: value, isCPU: false ) ) + return ( data.keyName, ( value: value, isCPU: false, isFan: regexFanRPM.firstMatch(in: data.keyName, range: NSMakeRange(0, data.keyName.count)) != nil ) ) } public func refresh( completion: @escaping () -> Void ) @@ -116,9 +118,11 @@ public class ThermalLog: NSObject #endif let sensors = self.readSensors() - let cpu = sensors.filter { $0.value.isCPU }.mapValues { $0.temperature } - let all = sensors.mapValues { $0.temperature } - self.sensors = all + let cpu = sensors.filter { $0.value.isCPU }.mapValues { $0.value } + let allTemperatures = sensors.filter { $0.value.isFan == false && ($0.value.value > 0 && $0.value.value < 120) }.mapValues { $0.value } + let allFans = sensors.filter { $0.value.isFan == true }.mapValues { $0.value + 10 } + self.sensors = allTemperatures + self.fans = allFans let names = UserDefaults.standard.object( forKey: "selectedSensors" ) as? [ String ] ?? [] let selectionMode = UserDefaults.standard.integer( forKey: "selectedSensorsMode" ) let selected = sensors.filter @@ -127,7 +131,7 @@ public class ThermalLog: NSObject } .mapValues { - $0.temperature + $0.value } let temperatures: [ Double ] = @@ -142,9 +146,9 @@ public class ThermalLog: NSObject } else { - let tcal = all.first { $0.key.lowercased().hasSuffix( "tcal" ) } + let tcal = allTemperatures.first { $0.key.lowercased().hasSuffix( "tcal" ) } - return all.filter + return allTemperatures.filter { if $0.key.lowercased().hasSuffix( "tcal" ) { @@ -186,6 +190,28 @@ public class ThermalLog: NSObject self.temperature = NSNumber( value: temp ) } + let fanSpeeds: [ Double ] = + { + return allFans.filter + {_ in + true + } + .values.map { $0 } + }() + + let fanSpeed: Double = + { + return fanSpeeds.reduce( 0.0 ) + { + r, v in max( r, v ) + } + }() + + if !fanSpeed.isNaN + { + self.fanSpeed = NSNumber( value: fanSpeed ) + } + let pipe = Pipe() let task = Process() task.launchPath = "/usr/bin/pmset" diff --git a/Hot/UI/Base.lproj/FanViewController.xib b/Hot/UI/Base.lproj/FanViewController.xib new file mode 100644 index 0000000..0738d3b --- /dev/null +++ b/Hot/UI/Base.lproj/FanViewController.xib @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FanSpeedToString + + + + + + + + + + + + + + + + + + + + + + diff --git a/Hot/UI/Base.lproj/InfoViewController.xib b/Hot/UI/Base.lproj/InfoViewController.xib index 29fe902..23a9abb 100644 --- a/Hot/UI/Base.lproj/InfoViewController.xib +++ b/Hot/UI/Base.lproj/InfoViewController.xib @@ -1,8 +1,8 @@ - + - + @@ -16,13 +16,13 @@ - + - + - + @@ -71,7 +71,7 @@ - + @@ -120,7 +120,7 @@ - + @@ -169,7 +169,7 @@ - + @@ -222,7 +222,7 @@ - + @@ -267,6 +267,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FanSpeedToString + + + + + + + + + + + + + + + + @@ -286,6 +332,7 @@ + @@ -294,6 +341,7 @@ + @@ -308,6 +356,7 @@ + diff --git a/Hot/UI/Base.lproj/MainMenu.xib b/Hot/UI/Base.lproj/MainMenu.xib index 175f552..be210de 100644 --- a/Hot/UI/Base.lproj/MainMenu.xib +++ b/Hot/UI/Base.lproj/MainMenu.xib @@ -2,7 +2,7 @@ - + @@ -14,6 +14,7 @@ + @@ -711,6 +712,23 @@ + + + + + + + + + + + + + NSNegateBoolean + + + + diff --git a/Hot/UI/GraphWindowController.xib b/Hot/UI/GraphWindowController.xib index e84ab69..c715d4f 100644 --- a/Hot/UI/GraphWindowController.xib +++ b/Hot/UI/GraphWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -18,7 +18,7 @@ - + @@ -27,26 +27,26 @@ - + - + - + @@ -95,7 +95,7 @@ - + @@ -144,7 +144,7 @@ - + @@ -197,7 +197,7 @@ - + @@ -242,6 +242,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FanSpeedToString + + + + + + + + + + + + + + + + @@ -252,12 +298,14 @@ + + @@ -277,6 +325,7 @@ + diff --git a/Hot/UI/Images/FanTemplate.pdf b/Hot/UI/Images/FanTemplate.pdf new file mode 100644 index 0000000000000000000000000000000000000000..574fed8e3d75d3117dc141cba58419494661f7cf GIT binary patch literal 3325 zcmai1c|25m8@J?@u~TX&PL^yjW5$fh9b}1QO_3~Rni)%mVHQi-y4Mm#S+XysY!xoD z4B|$%WGj&@3E8(75yE>$%k6#dAMfvTp7WgF_xGG<`#k6K`5qZ_J$)q=WfVlFbgp0% zq6)wP^5rWKEEa$n6WyI?&HxI8EFb^?!1P^6G$IAmBpi*XN5qo}M2My)gi50jac&Tw zR5xqGf=?BK12g+7Pqd!q%k<%qcrIW**AU3vzs7k;cWLWBB(h-5@C<=3I$FCSV5GK| zZKu0xuETY+!|Td?ZQ#PrmtMxsR&Bmr(;##54B`nX_X zdQ*rc{pkad*@|-fKJ2scGac_%G4n|H69ZSO*6r(&8(D|bw+ff#>;}fWv^c-m&b-82 z&9lzdK)>(0HpcqrNHWrFt+pe1CBn{~odTP$!rkXvADwbZ;qe*rg2BeRDP79sg*VV> z9)Wy^YPMPz&e01sn2;}l8!3M%MYfb?T(`NVbgk!1Yv%s#&s7?i#PhS(r{626b(AdX zXx;UAFElsgs?pB6hG~@8gq2Hj@i>bPi)xrqqX8xlUE zU=sbu1HmS`uiyt3T_qIa*Z0nkW5pN)ZX77tjsG?Zb<7F6wU?7 z%)&ao!iwn=PsAcz-^I~lOx5ZZZDbw$TP~gWf_6+flA#;ys_@YDvbI>EJjtZ-py5OA z;OL8mqcc+y()u}mCH6x=`g0F@&S*%fq5t4-M|f;HLm8Q31k_dlE#AS}RbOa4HyQ5O z)oC4aV_7!sQS33Lk#gd{m$|H}%H9c!<%)b1dJ`FxlCw7;lqX8jTPFI$2gcWt2?_h1 zQjQ990d0-Wp@eDc?RsPNeNt8DRlJ)}v)&tKe6xL(@Q`PkaB4q$9Ix)pBu}F3##XWNKkYhpn?j8QXANKPrA2 z!^par&VSw}Z~hKp*h5P}oi=*3VO3U-&utoGB4HE@HB%7q#EJy*cgFU{}5{ zLtbUym`x;1RBFu2*4{XhHNlum)*i2&m%^?Tugcoy75h#o^jJcSmu0zAPj}KM2enwdb1*GHNria{2DE*K-CAVWyCs1#+xPZE_W9%QJo)!1dR#R{acBI5Av_a{yKxHtr z`VDWLHj4Xm7NI2Y^z~a$R&7nLlZI0blRq$b5~uQY+Z6K z%d;N(8v*w7ob(SdV|&bt8)#gJ6i5c53ci#?ZP_Lt^bk9sX6Pdq#EZW_H)pYCwwYf@Ngo2Grh3wl~$OBUc}^ABM!;_ zjv0S>#jV2)Yvz2PSj21(rzMpik*$#1`8qp0)0alT7QA?ox07~xa7vE<#bo+lUh&>` zp>P*7DmH&DV)j{dspCRf`L#eVsJXI6N><3g&8L%x+phbDaL0&aYaB@4o*LIA!V9bI z{fCRR`?9mT8VcD9Vp4};x{jxG$Ztq9jqT$+~asdzgZ+BPvA32%4 zis=~Sae@@yiML9Tl!dFtDXvs(Xgv|2gRyn zL}czSb}m)c#0*}gjIO(9wDsDK=oFR(8xF%{%F<7a9dP44ce#0m2p%So2MoK1UJ zmL-VP{VFw4*GR^kn0Y*-qe*yqbKmSriFgE8^qDh^9`EVp`noCZk;dlNtNezl4K=0Z zi4VmD<%=q*v3-i=rEi}=7FKgx#*NDx+pO41`qa80C!{j--YB|C%Si3rceQ77?}2%! zFTecJ%)5Id%xgM(+JLae(PcNCUgPG;5tX&ICf+E&u(9)!F8S$@tWAVjgi_wn`WFid zx`-HH)>3uq^a)g0To@!X?6$0Q>5}hq*fDp8^P;Qx+t&fDN)N`tE`xY>r2GNw``^N&Rq4IKxMDT zLJoTw8wjyj$h!iX-QLQ9>3*`&5(ipl?OUqi?VK6!MbC|zY;2mMKAP0xbyNQ>b}_(h zXJDJ3U=>ONA7fHp}#ml|Kh-$LMG7hAVrnK<6J0YKt)*vtqcd`oM|)< zsyYm8FNiu(a30Psc&aj);v_G>8%P%vV4h~lVzmos8 zQ~M{Th60D=zwMAoDw literal 0 HcmV?d00001 From 4931ab3c64f8849861106acfd31e9f1e44ca8fee Mon Sep 17 00:00:00 2001 From: Tom Underhill Date: Sat, 27 Jul 2024 23:51:24 +0200 Subject: [PATCH 2/2] Remove test code --- Hot/Classes/ThermalLog.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hot/Classes/ThermalLog.swift b/Hot/Classes/ThermalLog.swift index ab37c61..c520bb6 100644 --- a/Hot/Classes/ThermalLog.swift +++ b/Hot/Classes/ThermalLog.swift @@ -120,7 +120,7 @@ public class ThermalLog: NSObject let sensors = self.readSensors() let cpu = sensors.filter { $0.value.isCPU }.mapValues { $0.value } let allTemperatures = sensors.filter { $0.value.isFan == false && ($0.value.value > 0 && $0.value.value < 120) }.mapValues { $0.value } - let allFans = sensors.filter { $0.value.isFan == true }.mapValues { $0.value + 10 } + let allFans = sensors.filter { $0.value.isFan == true }.mapValues { $0.value } self.sensors = allTemperatures self.fans = allFans let names = UserDefaults.standard.object( forKey: "selectedSensors" ) as? [ String ] ?? []