- looking how to decode fp2e;

- add different units for sensors;
- cleanup;
This commit is contained in:
Serhiy Mytrovtsiy
2020-04-06 23:55:29 +02:00
parent 413674db32
commit a4d5cf881b
12 changed files with 205 additions and 158 deletions

View File

@@ -1,3 +1,3 @@
github "sindresorhus/LaunchAtLogin"
github "sindresorhus/LaunchAtLogin" ~> 3.0.0
github "danielgindi/Charts" ~> 3.4.0
github "ashleymills/Reachability.swift"
github "ashleymills/Reachability.swift" ~> 5.0.0

View File

@@ -5,13 +5,14 @@ ITC_USERNAME = $(AC_USERNAME)
ITC_PASSWORD = @keychain:AC_PASSWORD
ITC_PROVIDER = $(AC_PROVIDER)
RequestUUID = e6c7b954-d9fa-4c74-8927-ba2172c9526e
RequestUUID = 32c80f6c-36ed-4042-9837-b9093c9f4eb9
BUILD_PATH = $(PWD)/build
ARCHIVE_PATH = $(BUILD_PATH)/$(APP).xcarchive
APP_PATH = "$(BUILD_PATH)/$(APP).app"
ZIP_PATH = "$(BUILD_PATH)/$(APP).zip"
DMG_PATH = $(PWD)/$(APP).dmg
LOCATION=$(BUILD_PATH)/$(APP).app/Contents/Library/LoginItems/LaunchAtLoginHelper.app
all: clean archive notarize sign build

View File

@@ -9,6 +9,7 @@ Stats is a application which allows you to monitor your macOS system.
- CPU Usage
- Memory Usage
- Sensors (Temperature/Voltage/Power)
- Disk utilization
- Battery level
- Network usage
@@ -23,6 +24,7 @@ You can download latest version [here](https://github.com/exelban/stats/releases
| --- | --- | --- |
| **CPU** | Percentage / Chart / Chart with value / Chart Bar | Shows CPU usage |
| **Memory** | Percentage / Chart / Chart with value / Chart Bar | Shows RAM usage |
| **Sensors** | Text | Shows data from internal sensors |
| **Disk** | Percentage / Chart Bar | Shows disk utilization |
| **Battery** | Graphic / Percentage | Shows battery level and charging status |
| **Newtork** | Dots / Upload/Download traffic | Shows network activity |
@@ -30,7 +32,7 @@ You can download latest version [here](https://github.com/exelban/stats/releases
## Compatibility
| macOS | Compatible |
| --- | --- |
| 10.15.2 *(Catalina)* | **true** |
| 10.15.3 *(Catalina)* | **true** |
| 10.14.6 *(Mojave)* | **true** |
| 10.13.6 *(High Sierra)* | **true** |

View File

@@ -400,8 +400,8 @@
9A1410F2229E721100D29793 /* Frameworks */,
9A1410F3229E721100D29793 /* Resources */,
9AB54DAE22A19F96006192E0 /* Copy Files */,
9A6698D82326A903001D00E1 /* ShellScript */,
9A493CE1232047F00064570C /* ShellScript */,
9A6698D82326A903001D00E1 /* Run Script */,
9A493CE1232047F00064570C /* Run Script */,
9A6698E72326AB16001D00E1 /* Embed Frameworks */,
);
buildRules = (
@@ -470,7 +470,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
9A493CE1232047F00064570C /* ShellScript */ = {
9A493CE1232047F00064570C /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -479,6 +479,7 @@
);
inputPaths = (
);
name = "Run Script";
outputFileListPaths = (
);
outputPaths = (
@@ -487,7 +488,7 @@
shellPath = /bin/sh;
shellScript = "\"${PROJECT_DIR}/Carthage/Build/Mac/LaunchAtLogin.framework/Resources/copy-helper.sh\"\n";
};
9A6698D82326A903001D00E1 /* ShellScript */ = {
9A6698D82326A903001D00E1 /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
@@ -498,6 +499,7 @@
"$(SRCROOT)/Carthage/Build/Mac/LaunchAtLogin.framework",
"$(SRCROOT)/Carthage/Build/Mac/Charts.framework",
);
name = "Run Script";
outputFileListPaths = (
);
outputPaths = (
@@ -704,6 +706,7 @@
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = RP2S87B72W;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = (
@@ -717,7 +720,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 1.5.2;
MARKETING_VERSION = 1.6.0;
PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -734,6 +737,7 @@
CODE_SIGN_IDENTITY = "Mac Developer";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = RP2S87B72W;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = (
@@ -747,7 +751,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 1.5.2;
MARKETING_VERSION = 1.6.0;
PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -48,7 +48,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
func applicationWillTerminate(_ aNotification: Notification) {
// SMCClose()
_ = smc.close()
menuBar?.destroy()
}

View File

@@ -71,8 +71,10 @@ class Sensors: Module {
}
private func update() {
var value_1: Double = 0
var value_2: Double = 0
var value_1_unit: Double = 0
var value_1_value: Double = 0
var value_2_unit: Double = 0
var value_2_value: Double = 0
var sensor_1: Sensor_t? = self.sensors.find(byKey: self.value_1)
var sensor_2: Sensor_t? = self.sensors.find(byKey: self.value_2)
@@ -80,18 +82,20 @@ class Sensors: Module {
if sensor_1 != nil {
sensor_1!.update()
if sensor_1!.value != nil {
value_1 = sensor_1!.value!
value_1_value = sensor_1!.value!
value_1_unit = Double(sensor_1!.unit[0].unicodeScalarCodePoint())
}
}
if sensor_2 != nil {
sensor_2!.update()
if sensor_2!.value != nil {
value_2 = sensor_2!.value!
value_2_value = sensor_2!.value!
value_2_unit = Double(sensor_2!.unit[0].unicodeScalarCodePoint())
}
}
DispatchQueue.main.async(execute: {
(self.widget.view as! Widget).setValue(data: [value_1, value_2])
(self.widget.view as! Widget).setValue(data: [value_1_value, value_1_unit, value_2_value, value_2_unit])
})
}
}

View File

@@ -19,6 +19,8 @@ enum SensorType: SensorType_t {
case Temperature = "Temperature"
case Voltage = "Voltage"
case Power = "Power"
case Frequency = "Frequency"
case Battery = "Battery"
}
struct Sensor_t {
@@ -27,6 +29,19 @@ struct Sensor_t {
var group: SensorGroup_t
var type: SensorType_t
var unit: String {
get {
switch self.type{
case SensorType.Temperature.rawValue:
return "°"
case SensorType.Voltage.rawValue:
return "V"
case SensorType.Power.rawValue:
return "W"
default: return ""
}
}
}
var value: Double? = nil
@@ -125,7 +140,6 @@ let SensorsDict: [String: Sensor_t] = [
"VG0C": Sensor_t(name: "GPU", group: SensorGroup.GPU.rawValue, type: SensorType.Voltage.rawValue),
"VM0R": Sensor_t(name: "Memory", group: SensorGroup.System.rawValue, type: SensorType.Voltage.rawValue),
"VBAT": Sensor_t(name: "Battery", group: SensorGroup.System.rawValue, type: SensorType.Voltage.rawValue),
"Vb0R": Sensor_t(name: "CMOS", group: SensorGroup.System.rawValue, type: SensorType.Voltage.rawValue),
"VD0R": Sensor_t(name: "DC In", group: SensorGroup.Sensor.rawValue, type: SensorType.Voltage.rawValue),
@@ -148,4 +162,22 @@ let SensorsDict: [String: Sensor_t] = [
"PPBR": Sensor_t(name: "Battery", group: SensorGroup.Sensor.rawValue, type: SensorType.Power.rawValue),
"PDTR": Sensor_t(name: "DC In", group: SensorGroup.Sensor.rawValue, type: SensorType.Power.rawValue),
"PSTR": Sensor_t(name: "System total", group: SensorGroup.Sensor.rawValue, type: SensorType.Power.rawValue),
/// Frequency
"FRC0": Sensor_t(name: "CPU 1", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue),
"FRC1": Sensor_t(name: "CPU 2", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue),
"FRC2": Sensor_t(name: "CPU 3", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue),
"FRC3": Sensor_t(name: "CPU 4", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue),
"FRC4": Sensor_t(name: "CPU 5", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue),
"FRC5": Sensor_t(name: "CPU 6", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue),
"FRC6": Sensor_t(name: "CPU 7", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue),
"FRC7": Sensor_t(name: "CPU 8", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue),
"CG0C": Sensor_t(name: "GPU", group: SensorGroup.GPU.rawValue, type: SensorType.Frequency.rawValue),
"CG0S": Sensor_t(name: "GPU shader", group: SensorGroup.GPU.rawValue, type: SensorType.Frequency.rawValue),
"CG0M": Sensor_t(name: "GPU memory", group: SensorGroup.GPU.rawValue, type: SensorType.Frequency.rawValue),
/// Battery
"B0AV": Sensor_t(name: "Voltage", group: SensorGroup.Sensor.rawValue, type: SensorType.Battery.rawValue),
"B0AC": Sensor_t(name: "Amperage", group: SensorGroup.Sensor.rawValue, type: SensorType.Battery.rawValue),
]

View File

@@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>

View File

@@ -7,6 +7,7 @@
//
import Cocoa
import Charts
class Chart: NSView, Widget {
public var name: String = "LineChart"
@@ -172,3 +173,9 @@ class Chart: NSView, Widget {
self.redraw()
}
}
class ChartsNetworkAxisFormatter: IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return Units(bytes: Int64(value)).getReadableSpeed()
}
}

View File

@@ -82,11 +82,13 @@ class SensorsWidget: NSView, Widget {
}
func setValue(data: [Double]) {
if self.value != data && data.count == 2 {
self.value = data
self.topValueView.stringValue = "\(Int(self.value[0]))°"
self.bottomValueView.stringValue = "\(Int(self.value[1]))°"
if self.value != data && data.count == 4 {
self.value = [data[0], data[2]]
let unit_1: String = String(UnicodeScalar(Int(data[1]))!)
let unit_2: String = String(UnicodeScalar(Int(data[3]))!)
self.topValueView.stringValue = "\(Int(self.value[0]))\(unit_1)"
self.bottomValueView.stringValue = "\(Int(self.value[1]))\(unit_2)"
self.topValueView.textColor = self.value[0].temperatureColor(color: self.color)
self.bottomValueView.textColor = self.value[1].temperatureColor(color: self.color)

View File

@@ -7,7 +7,123 @@
//
import Cocoa
import Charts
public enum Unit : Float {
case byte = 1
case kilobyte = 1024
case megabyte = 1048576
case gigabyte = 1073741824
}
public struct Units {
public let bytes: Int64
public init(bytes: Int64) {
self.bytes = bytes
}
public var kilobytes: Double {
return Double(bytes) / 1_024
}
public var megabytes: Double {
return kilobytes / 1_024
}
public var gigabytes: Double {
return megabytes / 1_024
}
public func getReadableTuple() -> (Double, String) {
switch bytes {
case 0..<1_024:
return (0, "KB/s")
case 1_024..<(1_024 * 1_024):
return (Double(String(format: "%.2f", kilobytes))!, "KB/s")
case 1_024..<(1_024 * 1_024 * 1_024):
return (Double(String(format: "%.2f", megabytes))!, "MB/s")
case (1_024 * 1_024 * 1_024)...Int64.max:
return (Double(String(format: "%.2f", gigabytes))!, "GB/s")
default:
return (Double(String(format: "%.2f", kilobytes))!, "KB/s")
}
}
public func getReadableSpeed() -> String {
switch bytes {
case 0..<1_024:
return "0 KB/s"
case 1_024..<(1_024 * 1_024):
return String(format: "%.0f KB/s", kilobytes)
case 1_024..<(1_024 * 1_024 * 100):
return String(format: "%.1f MB/s", megabytes)
case (1_024 * 1_024 * 100)..<(1_024 * 1_024 * 1_024):
return String(format: "%.0f MB/s", megabytes)
case (1_024 * 1_024 * 1_024)...Int64.max:
return String(format: "%.1f GB/s", gigabytes)
default:
return String(format: "%.0f KB/s", kilobytes)
}
}
public func getReadableMemory() -> String {
switch bytes {
case 0..<1_024:
return "0 KB/s"
case 1_024..<(1_024 * 1_024):
return String(format: "%.0f KB", kilobytes)
case 1_024..<(1_024 * 1_024 * 1_024):
return String(format: "%.0f MB", megabytes)
case (1_024 * 1_024 * 1_024)...Int64.max:
return String(format: "%.2f GB", gigabytes)
default:
return String(format: "%.0f KB", kilobytes)
}
}
}
extension String {
func condenseWhitespace() -> String {
let components = self.components(separatedBy: .whitespacesAndNewlines)
return components.filter { !$0.isEmpty }.joined(separator: " ")
}
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer(mutating: (self as NSString).utf8String!)
}
mutating func findAndCrop(pattern: String) -> String {
let regex = try! NSRegularExpression(pattern: pattern)
let stringRange = NSRange(location: 0, length: self.utf16.count)
var line = self
if let searchRange = regex.firstMatch(in: self, options: [], range: stringRange) {
let start = self.index(self.startIndex, offsetBy: searchRange.range.lowerBound)
let end = self.index(self.startIndex, offsetBy: searchRange.range.upperBound)
let value = String(self[start..<end]).trimmingCharacters(in: .whitespaces)
line = self.replacingOccurrences(
of: value,
with: "",
options: .regularExpression
)
self = line.trimmingCharacters(in: .whitespaces)
return value.trimmingCharacters(in: .whitespaces)
}
return ""
}
func matches(_ regex: String) -> Bool {
return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
}
func toUpperCase() -> String {
return prefix(1).capitalized + dropFirst()
}
func toLowwerCase() -> String {
return prefix(1).lowercased() + dropFirst()
}
subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
}
extension Double {
func roundTo(decimalPlaces: Int) -> String {
@@ -85,81 +201,6 @@ extension Double {
func splitAtDecimal() -> [Int64] {
return "\(self)".split(separator: ".").map{Int64($0)!}
}
}
public enum Unit : Float {
case byte = 1
case kilobyte = 1024
case megabyte = 1048576
case gigabyte = 1073741824
}
public struct Units {
public let bytes: Int64
public init(bytes: Int64) {
self.bytes = bytes
}
public var kilobytes: Double {
return Double(bytes) / 1_024
}
public var megabytes: Double {
return kilobytes / 1_024
}
public var gigabytes: Double {
return megabytes / 1_024
}
public func getReadableTuple() -> (Double, String) {
switch bytes {
case 0..<1_024:
return (0, "KB/s")
case 1_024..<(1_024 * 1_024):
return (Double(String(format: "%.2f", kilobytes))!, "KB/s")
case 1_024..<(1_024 * 1_024 * 1_024):
return (Double(String(format: "%.2f", megabytes))!, "MB/s")
case (1_024 * 1_024 * 1_024)...Int64.max:
return (Double(String(format: "%.2f", gigabytes))!, "GB/s")
default:
return (Double(String(format: "%.2f", kilobytes))!, "KB/s")
}
}
public func getReadableSpeed() -> String {
switch bytes {
case 0..<1_024:
return "0 KB/s"
case 1_024..<(1_024 * 1_024):
return String(format: "%.0f KB/s", kilobytes)
case 1_024..<(1_024 * 1_024 * 100):
return String(format: "%.1f MB/s", megabytes)
case (1_024 * 1_024 * 100)..<(1_024 * 1_024 * 1_024):
return String(format: "%.0f MB/s", megabytes)
case (1_024 * 1_024 * 1_024)...Int64.max:
return String(format: "%.1f GB/s", gigabytes)
default:
return String(format: "%.0f KB/s", kilobytes)
}
}
public func getReadableMemory() -> String {
switch bytes {
case 0..<1_024:
return "0 KB/s"
case 1_024..<(1_024 * 1_024):
return String(format: "%.0f KB", kilobytes)
case 1_024..<(1_024 * 1_024 * 1_024):
return String(format: "%.0f MB", megabytes)
case (1_024 * 1_024 * 1_024)...Int64.max:
return String(format: "%.2f GB", gigabytes)
default:
return String(format: "%.0f KB", kilobytes)
}
}
}
extension Double {
func secondsToHoursMinutesSeconds () -> (Int?, Int?, Int?) {
let hrs = self / 3600
@@ -192,17 +233,6 @@ extension Double {
}
}
extension String {
func condenseWhitespace() -> String {
let components = self.components(separatedBy: .whitespacesAndNewlines)
return components.filter { !$0.isEmpty }.joined(separator: " ")
}
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer(mutating: (self as NSString).utf8String!)
}
}
extension NSBezierPath {
func addArrow(start: CGPoint, end: CGPoint, pointerLineLength: CGFloat, arrowAngle: CGFloat) {
self.move(to: start)
@@ -219,7 +249,6 @@ extension NSBezierPath {
}
extension NSColor {
convenience init(hexString: String, alpha: CGFloat = 1.0) {
let hexString: String = hexString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let scanner = Scanner(string: hexString)
@@ -249,53 +278,13 @@ extension NSColor {
}
}
extension String {
mutating func findAndCrop(pattern: String) -> String {
let regex = try! NSRegularExpression(pattern: pattern)
let stringRange = NSRange(location: 0, length: self.utf16.count)
var line = self
if let searchRange = regex.firstMatch(in: self, options: [], range: stringRange) {
let start = self.index(self.startIndex, offsetBy: searchRange.range.lowerBound)
let end = self.index(self.startIndex, offsetBy: searchRange.range.upperBound)
let value = String(self[start..<end]).trimmingCharacters(in: .whitespaces)
line = self.replacingOccurrences(
of: value,
with: "",
options: .regularExpression
)
self = line.trimmingCharacters(in: .whitespaces)
return value.trimmingCharacters(in: .whitespaces)
}
return ""
}
func matches(_ regex: String) -> Bool {
return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
}
func toUpperCase() -> String {
return prefix(1).capitalized + dropFirst()
}
func toLowwerCase() -> String {
return prefix(1).lowercased() + dropFirst()
}
}
extension URL {
func checkFileExist() -> Bool {
return FileManager.default.fileExists(atPath: self.path)
}
}
class ChartsNetworkAxisFormatter: IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return Units(bytes: Int64(value)).getReadableSpeed()
}
}
public extension FourCharCode {
extension FourCharCode {
init(fromString str: String) {
precondition(str.count == 4)
@@ -340,3 +329,12 @@ extension NSMenuItem {
}
}
}
extension Character {
func unicodeScalarCodePoint() -> UInt32 {
let characterString = String(self)
let scalars = characterString.unicodeScalars
return scalars[scalars.startIndex].value
}
}

View File

@@ -14,6 +14,7 @@ enum SMCDataType: String {
case SP87 = "sp87"
case FLT = "flt "
case FPE2 = "fpe2"
case FP2E = "fp2e"
}
enum SMCKeys: UInt8 {
@@ -151,12 +152,8 @@ class SMCService {
return Double(value!)
}
return nil
case SMCDataType.FPE2.rawValue:
// ntohs(*(UInt16*)val.bytes) / 4.0;
print("FPE2")
break
default:
print("unsupported data type \(val.dataType)")
print("unsupported data type \(val.dataType) for key: \(key)")
return nil
}
}