mirror of
https://github.com/AppHouseKitchen/AlDente-Battery_Care_and_Monitoring.git
synced 2026-07-14 16:52:49 +02:00
Apple Silicon Support
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// AlDente
|
||||
//
|
||||
// Created by David Wernhart on 09.02.20.
|
||||
// Copyright © 2020 David Wernhart. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
import LaunchAtLogin
|
||||
import Foundation
|
||||
import IOKit.ps
|
||||
import IOKit.pwr_mgt
|
||||
|
||||
extension ProcessInfo {
|
||||
/// Returns a `String` representing the machine hardware name or nil if there was an error invoking `uname(_:)` or decoding the response.
|
||||
///
|
||||
/// Return value is the equivalent to running `$ uname -m` in shell.
|
||||
var machineHardwareName: String? {
|
||||
var sysinfo = utsname()
|
||||
let result = uname(&sysinfo)
|
||||
guard result == EXIT_SUCCESS else { return nil }
|
||||
let data = Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN))
|
||||
guard let identifier = String(bytes: data, encoding: .ascii) else { return nil }
|
||||
return identifier.trimmingCharacters(in: .controlCharacters)
|
||||
}
|
||||
}
|
||||
|
||||
@NSApplicationMain
|
||||
final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
//var window: NSWindow!
|
||||
var statusBarItem: NSStatusItem!
|
||||
var popover: NSPopover!
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
Helper.instance.enableSleep()
|
||||
Helper.instance.enableCharging()
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
|
||||
let contentView = ContentView()
|
||||
|
||||
// Create the popover
|
||||
let popover = NSPopover()
|
||||
popover.contentSize = NSSize(width: 400, height: 600)
|
||||
popover.behavior = .transient
|
||||
popover.contentViewController = NSHostingController(rootView: contentView)
|
||||
self.popover = popover
|
||||
|
||||
let statusBar = NSStatusBar.system
|
||||
statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength)
|
||||
statusBarItem.button?.image = NSImage(named: "menubaricon")!
|
||||
statusBarItem.button?.action = #selector(togglePopover(_:))
|
||||
|
||||
Helper.instance.setPlatformKey()
|
||||
|
||||
Helper.instance.checkHelperVersion{(foundHelper) in
|
||||
if(foundHelper){
|
||||
print("helper found!")
|
||||
}
|
||||
else{
|
||||
Helper.instance.installHelper()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchAtLogin.isEnabled = true
|
||||
SMCPresenter.shared.loadValue()
|
||||
|
||||
Helper.instance.checkCharging()
|
||||
|
||||
var actionMsg:String?
|
||||
|
||||
Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { timer in
|
||||
if(Helper.instance.isInitialized){
|
||||
Helper.instance.getChargingInfo { (Name, Capacity, IsCharging, MaxCapacity) in
|
||||
|
||||
|
||||
if(!PersistanceManager.instance.oldKey){
|
||||
if(Capacity < SMCPresenter.shared.value){
|
||||
actionMsg = "NEED TO CHARGE"
|
||||
if(Helper.instance.chargeInhibited){
|
||||
Helper.instance.enableCharging()
|
||||
}
|
||||
Helper.instance.disableSleep()
|
||||
|
||||
}
|
||||
else{
|
||||
actionMsg = "IS PERFECT"
|
||||
if(!Helper.instance.chargeInhibited){
|
||||
Helper.instance.disableCharging()
|
||||
}
|
||||
Helper.instance.enableSleep()
|
||||
|
||||
}
|
||||
print("TARGET: ",SMCPresenter.shared.value,
|
||||
" CURRENT: ",String(Capacity),
|
||||
" ISCHARGING: ",String(IsCharging),
|
||||
" CHARGE INHIBITED: ",String(Helper.instance.chargeInhibited),
|
||||
" ACTION: ",actionMsg!)
|
||||
}
|
||||
else{
|
||||
print("BCLM MODE ENABLED")
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
Helper.instance.setStatusString()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@objc func togglePopover(_ sender: AnyObject?) {
|
||||
popover.contentViewController?.view.window?.becomeKey()
|
||||
|
||||
Helper.instance.setStatusString()
|
||||
if let button = self.statusBarItem.button {
|
||||
if popover.isShown {
|
||||
popover.performClose(sender)
|
||||
} else {
|
||||
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "spaghetti.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "aldentelighticon1x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "aldentedarkicon1x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "aldentelighticon2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "aldentedarkicon2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Application-->
|
||||
<scene sceneID="JPo-4y-FX3">
|
||||
<objects>
|
||||
<application id="hnw-xV-0zn" sceneMemberID="viewController">
|
||||
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="AlDente" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="AlDente" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About AlDente" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
<menuItem title="Services" id="NMo-om-nkz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Hide AlDente" keyEquivalent="h" id="Olw-nP-bQN">
|
||||
<connections>
|
||||
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||
<menuItem title="Quit AlDente" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="File" id="dMs-cI-mzQ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Edit" id="5QF-Oa-p0T">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Format" id="jxT-CU-nIS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="View" id="H8h-7b-M4v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Help" id="wpr-3q-Mcd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
|
||||
</connections>
|
||||
</application>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="AlDente" customModuleProvider="target"/>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="0.0"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,259 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// AlDente
|
||||
//
|
||||
// Created by David Wernhart on 09.02.20.
|
||||
// Copyright © 2020 David Wernhart. All rights reserved.
|
||||
//
|
||||
|
||||
import LaunchAtLogin
|
||||
import SwiftUI
|
||||
|
||||
private struct BlueButtonStyle: ButtonStyle {
|
||||
func makeBody(configuration: Self.Configuration) -> some View {
|
||||
configuration.label
|
||||
.foregroundColor(configuration.isPressed ? Color.blue : Color.white)
|
||||
.background(configuration.isPressed ? Color.white : Color.blue)
|
||||
.cornerRadius(6.0)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
private struct RedButtonStyle: ButtonStyle {
|
||||
func makeBody(configuration: Self.Configuration) -> some View {
|
||||
configuration.label
|
||||
.foregroundColor(configuration.isPressed ? Color.red : Color.white)
|
||||
.background(configuration.isPressed ? Color.white : Color.red)
|
||||
.cornerRadius(6.0)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
private var allowDischarge = true
|
||||
|
||||
private struct Settings: View {
|
||||
@State private var launchAtLogin = LaunchAtLogin.isEnabled
|
||||
@State private var oldKey = PersistanceManager.instance.oldKey
|
||||
@ObservedObject private var presenter = SMCPresenter.shared
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Spacer().frame(height: 15)
|
||||
Text(presenter.status)
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Toggle(isOn: Binding(
|
||||
get: { launchAtLogin },
|
||||
|
||||
set: { newValue in
|
||||
launchAtLogin = newValue
|
||||
print("Launch at login turned \(newValue ? "on" : "off")!")
|
||||
LaunchAtLogin.isEnabled = newValue
|
||||
}
|
||||
)) {
|
||||
Text("Launch at login")
|
||||
}
|
||||
|
||||
if(!Helper.instance.appleSilicon!){
|
||||
Toggle(isOn: Binding(
|
||||
get: { oldKey },
|
||||
|
||||
set: { newValue in
|
||||
oldKey = newValue
|
||||
PersistanceManager.instance.oldKey = oldKey
|
||||
PersistanceManager.instance.save()
|
||||
Helper.instance.setStatusString()
|
||||
if(newValue){
|
||||
Helper.instance.enableCharging()
|
||||
Helper.instance.enableSleep()
|
||||
//presenter.writeValue()
|
||||
}
|
||||
else{
|
||||
presenter.setValue(value: 100)
|
||||
}
|
||||
}
|
||||
)) {
|
||||
Text("Use Classic SMC Key (Intel)")
|
||||
}
|
||||
}
|
||||
}.padding()
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
Helper.instance.installHelper()
|
||||
}) {
|
||||
Text("Reinstall Helper")
|
||||
.frame(maxWidth: 120, maxHeight: 30)
|
||||
}.buttonStyle(BlueButtonStyle())
|
||||
}
|
||||
|
||||
HStack {
|
||||
Spacer().frame(width: 15)
|
||||
VStack(alignment: .leading) {
|
||||
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
||||
Text("AlDente \(version ?? "") 🍝").font(.headline)
|
||||
|
||||
let address = "github.com/davidwernhart/AlDente"
|
||||
Button(action: {
|
||||
openURL("https://" + address)
|
||||
}) {
|
||||
Text(address).foregroundColor(Color(.linkColor))
|
||||
}.buttonStyle(PlainButtonStyle())
|
||||
|
||||
Text("Cooked up in 2021 by AppHouseKitchen")
|
||||
// Text("AlDente 🍝").font(.title)
|
||||
// Text("Keep your battery just right").font(.subheadline)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
openURL("https://apphousekitchen.com/aldente/")
|
||||
}) {
|
||||
Text("Get Pro 🍜")
|
||||
.frame(maxWidth: 100, maxHeight: 50)
|
||||
}
|
||||
.buttonStyle(BlueButtonStyle())
|
||||
}
|
||||
}
|
||||
.background(Color(.unemphasizedSelectedContentBackgroundColor))
|
||||
.cornerRadius(5)
|
||||
}
|
||||
|
||||
private func openURL(_ string: String) {
|
||||
let url = URL(string: string)!
|
||||
if NSWorkspace.shared.open(url) {
|
||||
print("default browser was successfully opened")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@State private var adaptableHeight = CGFloat(100)
|
||||
@State private var showSettings = false
|
||||
|
||||
@ObservedObject private var presenter = SMCPresenter.shared
|
||||
|
||||
init() {
|
||||
Helper.instance.delegate = presenter
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
HStack {
|
||||
Text("Max. Battery Charge:").padding(.leading)
|
||||
TextField("Number", value: Binding(
|
||||
get: {
|
||||
Float(presenter.value)
|
||||
},
|
||||
set: { newValue in
|
||||
if newValue >= 20 && newValue <= 100 {
|
||||
presenter.setValue(value: newValue)
|
||||
}
|
||||
}
|
||||
), formatter: NumberFormatter())
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(maxWidth: 50)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
showSettings.toggle()
|
||||
adaptableHeight = showSettings ? 275 : 100
|
||||
}) {
|
||||
Text("Settings")
|
||||
.frame(maxWidth: 70, maxHeight: 30)
|
||||
}.buttonStyle(BlueButtonStyle()).padding(.leading, -30)
|
||||
|
||||
Button(action: {
|
||||
NSApplication.shared.terminate(self)
|
||||
}) {
|
||||
Text("Quit")
|
||||
.frame(maxWidth: 50, maxHeight: 30)
|
||||
}.buttonStyle(RedButtonStyle()).padding(.leading, -30)
|
||||
}
|
||||
|
||||
Slider(value: Binding(
|
||||
get: {
|
||||
Float(presenter.value)
|
||||
},
|
||||
set: { newValue in
|
||||
if newValue >= 20 && newValue <= 100 {
|
||||
presenter.setValue(value: newValue)
|
||||
}
|
||||
}
|
||||
), in: 20...100).padding(.horizontal).padding(.top, -20)
|
||||
|
||||
Spacer()
|
||||
|
||||
if showSettings {
|
||||
Settings()
|
||||
}
|
||||
|
||||
}.frame(width: 400, height: adaptableHeight)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public final class SMCPresenter: ObservableObject, HelperDelegate {
|
||||
|
||||
static let shared = SMCPresenter()
|
||||
|
||||
@Published var value: UInt8 = 0
|
||||
@Published var status: String = ""
|
||||
private var timer: Timer?
|
||||
private var accuracyTimer: Timer?
|
||||
|
||||
func OnMaxBatRead(value: UInt8) {
|
||||
if(PersistanceManager.instance.oldKey){
|
||||
DispatchQueue.main.async {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateStatus(status:String){
|
||||
DispatchQueue.main.async {
|
||||
self.status = status
|
||||
}
|
||||
}
|
||||
|
||||
public func loadValue(){
|
||||
PersistanceManager.instance.load()
|
||||
self.value = UInt8(PersistanceManager.instance.chargeVal!)
|
||||
if(self.value == 0){
|
||||
self.value = 50
|
||||
}
|
||||
print("loaded max charge val: ",self.value," old key:",PersistanceManager.instance.oldKey)
|
||||
// if(!Helper.instance.appleSilicon!){
|
||||
// Helper.instance.getSMCCharge(withReply: { (smcval) in
|
||||
// self.value = UInt8(smcval)
|
||||
// })
|
||||
// }
|
||||
if(PersistanceManager.instance.oldKey){
|
||||
writeValue()
|
||||
}
|
||||
}
|
||||
|
||||
func setValue(value: Float) {
|
||||
DispatchQueue.main.async {
|
||||
self.value = UInt8(value)
|
||||
PersistanceManager.instance.chargeVal = Int(value)
|
||||
PersistanceManager.instance.save()
|
||||
self.writeValue()
|
||||
}
|
||||
timer?.invalidate()
|
||||
accuracyTimer?.invalidate()
|
||||
}
|
||||
|
||||
func writeValue(){
|
||||
if(PersistanceManager.instance.oldKey){
|
||||
print("should write bclm value: ", self.value)
|
||||
Helper.instance.writeMaxBatteryCharge(setVal: self.value)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
//
|
||||
// Helper.swift
|
||||
// AlDente
|
||||
//
|
||||
// Created by David Wernhart on 14.02.20.
|
||||
// Copyright © 2020 David Wernhart. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ServiceManagement
|
||||
import IOKit.pwr_mgt
|
||||
|
||||
protocol HelperDelegate {
|
||||
func OnMaxBatRead(value: UInt8)
|
||||
func updateStatus(status:String)
|
||||
}
|
||||
|
||||
final class Helper {
|
||||
|
||||
static let instance = Helper()
|
||||
|
||||
public var delegate: HelperDelegate?
|
||||
|
||||
private var key: String?
|
||||
|
||||
private var preventSleepID: IOPMAssertionID?
|
||||
|
||||
public var appleSilicon:Bool?
|
||||
public var chargeInhibited: Bool = false
|
||||
public var isInitialized:Bool = false
|
||||
|
||||
public var statusString:String = ""
|
||||
|
||||
|
||||
lazy var helperToolConnection: NSXPCConnection = {
|
||||
let connection = NSXPCConnection(machServiceName: "com.davidwernhart.Helper.mach", options: .privileged)
|
||||
connection.remoteObjectInterface = NSXPCInterface(with: HelperToolProtocol.self)
|
||||
|
||||
connection.resume()
|
||||
return connection
|
||||
}()
|
||||
|
||||
func setPlatformKey() {
|
||||
let s:String! = ProcessInfo.init().machineHardwareName
|
||||
if(s != nil){
|
||||
if(s.elementsEqual("x86_64")){
|
||||
print("intel cpu!")
|
||||
appleSilicon = false;
|
||||
}
|
||||
else if(s.elementsEqual("arm64")){
|
||||
print("arm cpu!")
|
||||
appleSilicon = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
func setStatusString(){
|
||||
checkCharging()
|
||||
var sleepDisabled:Bool = !(preventSleepID == nil)
|
||||
statusString = ""
|
||||
if(PersistanceManager.instance.oldKey){
|
||||
statusString = "BCLM Key Mode. Final charge value can differ by up to 5%"
|
||||
}
|
||||
else{
|
||||
statusString = "Charge Inhibit: "+String(chargeInhibited)+" | Prevent Sleep: "+String(sleepDisabled)+" | Helper v"+String(helperVersion)+": \(self.isInitialized ? "found" : "not found")"
|
||||
}
|
||||
|
||||
|
||||
self.delegate?.updateStatus(status: statusString)
|
||||
}
|
||||
|
||||
|
||||
func enableSleep(){
|
||||
if(self.preventSleepID != nil){
|
||||
print("RELEASING PREVENT SLEEP ASSERTION WITH ID: ",preventSleepID!)
|
||||
releaseAssertion(assertionId: self.preventSleepID!)
|
||||
self.preventSleepID = nil
|
||||
}
|
||||
}
|
||||
|
||||
func disableSleep(){
|
||||
createAssertion(assertion: kIOPMAssertionTypePreventSystemSleep){ id in
|
||||
if(self.preventSleepID == nil){
|
||||
print("PREVENT SLEEP ASSERTION CREATED! ID: ",id)
|
||||
self.preventSleepID = id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enableCharging(){
|
||||
if(appleSilicon!){
|
||||
SMCWriteByte(key: "CH0B", value: 00)
|
||||
}
|
||||
SMCWriteByte(key: "CH0B", value: 00)
|
||||
self.chargeInhibited = false
|
||||
|
||||
}
|
||||
|
||||
func disableCharging(){
|
||||
if(appleSilicon!){
|
||||
SMCWriteByte(key: "CH0B", value: 02)
|
||||
}
|
||||
SMCWriteByte(key: "CH0B", value: 02)
|
||||
self.chargeInhibited = true
|
||||
|
||||
}
|
||||
|
||||
func checkCharging(){
|
||||
Helper.instance.SMCReadUInt32(key: "CH0B") { value in
|
||||
self.chargeInhibited = !(value == 00)
|
||||
print("CHARGE INHIBITED: "+String(self.chargeInhibited))
|
||||
}
|
||||
if(PersistanceManager.instance.oldKey){
|
||||
Helper.instance.readMaxBatteryCharge()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getChargingInfo(withReply reply: (String,Int,Bool,Int) -> Void){
|
||||
let snapshot = IOPSCopyPowerSourcesInfo().takeRetainedValue()
|
||||
let sources = IOPSCopyPowerSourcesList(snapshot).takeRetainedValue() as Array
|
||||
let info = IOPSGetPowerSourceDescription(snapshot, sources[0]).takeUnretainedValue() as! [String: AnyObject]
|
||||
|
||||
if let name = info[kIOPSNameKey] as? String,
|
||||
let capacity = info[kIOPSCurrentCapacityKey] as? Int,
|
||||
let isCharging = info[kIOPSIsChargingKey] as? Bool,
|
||||
let max = info[kIOPSMaxCapacityKey] as? Int {
|
||||
reply(name,capacity,isCharging,max)
|
||||
}
|
||||
}
|
||||
|
||||
func getSMCCharge(withReply reply: @escaping (Float)->Void){
|
||||
Helper.instance.SMCReadUInt32(key: "BRSC") { value in
|
||||
let smcval = Float(value >> 16)
|
||||
reply(smcval)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func createAssertion(assertion: String, withReply reply: @escaping (IOPMAssertionID) -> Void){
|
||||
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
||||
let e = $0 as NSError
|
||||
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
||||
} as? HelperToolProtocol
|
||||
|
||||
helper?.createAssertion(assertion: assertion, withReply: { id in
|
||||
reply(id)
|
||||
})
|
||||
}
|
||||
|
||||
@objc func releaseAssertion(assertionId: IOPMAssertionID){
|
||||
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
||||
let e = $0 as NSError
|
||||
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
||||
} as? HelperToolProtocol
|
||||
|
||||
helper?.releaseAssertion(assertionID: assertionId)
|
||||
}
|
||||
|
||||
@objc func installHelper() {
|
||||
print("trying to install helper!")
|
||||
var status = noErr
|
||||
let helperID = "com.davidwernhart.Helper" as CFString // Prefs.helperID as CFString
|
||||
|
||||
var authItem = kSMRightBlessPrivilegedHelper.withCString {
|
||||
AuthorizationItem(name: $0, valueLength: 0, value: nil, flags: 0)
|
||||
}
|
||||
var authRights = withUnsafeMutablePointer(to: &authItem) {
|
||||
AuthorizationRights(count: 1, items: $0)
|
||||
}
|
||||
let authFlags: AuthorizationFlags = [.interactionAllowed, .preAuthorize, .extendRights]
|
||||
var authRef: AuthorizationRef?
|
||||
status = AuthorizationCreate(&authRights, nil, authFlags, &authRef)
|
||||
if status != errAuthorizationSuccess {
|
||||
print(SecCopyErrorMessageString(status, nil) ?? "")
|
||||
print("Error: \(status)")
|
||||
}
|
||||
|
||||
var error: Unmanaged<CFError>?
|
||||
SMJobBless(kSMDomainSystemLaunchd, helperID, authRef, &error)
|
||||
if let e = error?.takeRetainedValue() {
|
||||
print("Domain: ", CFErrorGetDomain(e) ?? "")
|
||||
print("Code: ", CFErrorGetCode(e))
|
||||
print("UserInfo: ", CFErrorCopyUserInfo(e) ?? "")
|
||||
print("Description: ", CFErrorCopyDescription(e) ?? "")
|
||||
print("Reason: ", CFErrorCopyFailureReason(e) ?? "")
|
||||
print("Suggestion: ", CFErrorCopyRecoverySuggestion(e) ?? "")
|
||||
}
|
||||
|
||||
if(error == nil){
|
||||
print("helper installed successfully!")
|
||||
restart()
|
||||
}
|
||||
}
|
||||
|
||||
func restart(){
|
||||
let url = URL(fileURLWithPath: Bundle.main.resourcePath!)
|
||||
let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString
|
||||
let task = Process()
|
||||
task.launchPath = "/usr/bin/open"
|
||||
task.arguments = [path]
|
||||
task.launch()
|
||||
exit(0)
|
||||
}
|
||||
|
||||
@objc func setResetValues(){
|
||||
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
||||
let e = $0 as NSError
|
||||
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
||||
|
||||
} as? HelperToolProtocol
|
||||
|
||||
helper?.setResetVal(key: "CH0B", value: 00)
|
||||
}
|
||||
|
||||
@objc func writeMaxBatteryCharge(setVal: UInt8) {
|
||||
SMCWriteByte(key: "BCLM", value: setVal)
|
||||
|
||||
}
|
||||
|
||||
@objc func readMaxBatteryCharge() {
|
||||
SMCReadByte(key: "BCLM") { value in
|
||||
print("OLD KEY MAX CHARGE: "+String(value))
|
||||
self.delegate?.OnMaxBatRead(value: value)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func enableCharging(enabled: Bool) {
|
||||
if(enabled){
|
||||
SMCWriteByte(key: "CH0B", value: 00)
|
||||
}
|
||||
else{
|
||||
SMCWriteByte(key: "CH0B", value: 02)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@objc func checkHelperVersion(withReply reply: @escaping (Bool) -> Void) {
|
||||
print("checking helper version")
|
||||
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
||||
let e = $0 as NSError
|
||||
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
||||
reply(false)
|
||||
return()
|
||||
|
||||
} as? HelperToolProtocol
|
||||
|
||||
helper?.getVersion { version in
|
||||
print("helperVersion:", helperVersion, " version from helper:", version)
|
||||
if !helperVersion.elementsEqual(version) {
|
||||
reply(false)
|
||||
return() }
|
||||
else{
|
||||
self.isInitialized = true
|
||||
reply(true)
|
||||
return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func SMCReadByte(key: String, withReply reply: @escaping (UInt8) -> Void) {
|
||||
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
||||
let e = $0 as NSError
|
||||
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
||||
|
||||
} as? HelperToolProtocol
|
||||
|
||||
helper?.readSMCByte(key: key) {
|
||||
reply($0)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func SMCReadUInt32(key: String, withReply reply: @escaping (UInt32) -> Void) {
|
||||
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
||||
let e = $0 as NSError
|
||||
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
||||
|
||||
} as? HelperToolProtocol
|
||||
|
||||
helper?.readSMCUInt32(key: key) {
|
||||
reply($0)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func SMCWriteByte(key: String, value: UInt8) {
|
||||
let helper = helperToolConnection.remoteObjectProxyWithErrorHandler {
|
||||
let e = $0 as NSError
|
||||
print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")")
|
||||
|
||||
} as? HelperToolProtocol
|
||||
|
||||
helper?.setSMCByte(key: key, value: value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2020 David Wernhart. All rights reserved.</string>
|
||||
<key>NSMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSSupportsAutomaticTermination</key>
|
||||
<false/>
|
||||
<key>NSSupportsSuddenTermination</key>
|
||||
<false/>
|
||||
<key>SMPrivilegedExecutables</key>
|
||||
<dict>
|
||||
<key>com.davidwernhart.Helper</key>
|
||||
<string>identifier "com.davidwernhart.Helper" and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: david.wernhart96@gmail.com (GSDX9BQ584)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// PersistanceManager.swift
|
||||
// AlDente
|
||||
//
|
||||
// Created by David Wernhart on 07.02.21.
|
||||
// Copyright © 2021 David Wernhart. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class PersistanceManager{
|
||||
static let instance = PersistanceManager()
|
||||
|
||||
public var launchOnLogin: Bool?
|
||||
public var chargeVal: Int?
|
||||
public var oldKey: Bool = false
|
||||
|
||||
public func load(){
|
||||
launchOnLogin = UserDefaults.standard.bool(forKey: "launchOnLogin")
|
||||
oldKey = UserDefaults.standard.bool(forKey: "oldKey")
|
||||
chargeVal = UserDefaults.standard.integer(forKey: "chargeVal")
|
||||
}
|
||||
|
||||
public func save(){
|
||||
UserDefaults.standard.set(launchOnLogin, forKey: "launchOnLogin")
|
||||
UserDefaults.standard.set(chargeVal, forKey: "chargeVal")
|
||||
UserDefaults.standard.set(oldKey, forKey: "oldKey")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user