Skip to content

Commit

Permalink
Bump build and implement Mac Automations
Browse files Browse the repository at this point in the history
Lots of changes but I'll attempt to get them all here.
- Move MenuBarExtraView to MacSupport directory
- MacSettingsUtils for GetMacAutomationSetting, for easily getting Mac Automation Settings
- MacIOKit now provides GetMacIsCharging, GetMacPowerSource and HandleMacPowerStateChange, see the file for docs
- MacAppDelegate uses these new functions
- AutomationsView for configuration of Mac Automation Settings currently, other platforms get a notice
- ContentView shows the AutomationsView now
- DefaultsUsed now documents the new used defaults
- Maybe more I forgot, but check the diff if unsure
  • Loading branch information
ThatStella7922 committed Nov 22, 2023
1 parent e542b3f commit 5764d7e
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 5 deletions.
4 changes: 2 additions & 2 deletions dcbattwebhook-swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "dcbattwebhook-swift/dcbattwebhook_swift.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CURRENT_PROJECT_VERSION = 48;
CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_ASSET_PATHS = "\"dcbattwebhook-swift/Preview Content\"";
ENABLE_HARDENED_RUNTIME = NO;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
Expand Down Expand Up @@ -641,7 +641,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "dcbattwebhook-swift/dcbattwebhook_swift.entitlements";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CURRENT_PROJECT_VERSION = 48;
CURRENT_PROJECT_VERSION = 50;
DEVELOPMENT_ASSET_PATHS = "\"dcbattwebhook-swift/Preview Content\"";
ENABLE_HARDENED_RUNTIME = NO;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
Expand Down
Binary file not shown.
115 changes: 115 additions & 0 deletions dcbattwebhook-swift/AutomationsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// AutomationsView.swift
// dcbattwebhook-swift
//
// Created by Stella Luna on 11/22/23.
//

import SwiftUI

struct AutomationsView: View {
@State private var macSendOnPluggedIn = false
@State private var macSendOnUnplugged = false
@State private var macSendOnHitFullCharge = false

private var macSendSettings: [Bool] {[
macSendOnPluggedIn,
macSendOnUnplugged,
macSendOnHitFullCharge
]}

let defaults = UserDefaults.standard

var body: some View {
VStack {
#if os(macOS)
Form {
Section(header: Text("Events"), footer: Text("You can choose to automatically send battery info when one or more of these things happen.\nConfigure Display name, pronoun, etc. in Settings.")) {
Text("Send battery info automatically when...")
Toggle(isOn: $macSendOnPluggedIn) {
Text("Device is plugged in")
}
Toggle(isOn: $macSendOnUnplugged) {
Text("Device is unplugged")
}
Toggle(isOn: $macSendOnHitFullCharge) {
Text("Device finishes charging")
}.disabled(true)
}
}.formStyle(.grouped)
.onAppear() {
if defaults.object(forKey: "MacSendOnPluggedIn") == nil {
macSendOnPluggedIn = false
} else { macSendOnPluggedIn = defaults.bool(forKey: "MacSendOnPluggedIn") }
if defaults.object(forKey: "MacSendOnUnplugged") == nil {
macSendOnUnplugged = false
} else { macSendOnUnplugged = defaults.bool(forKey: "MacSendOnUnplugged") }
if defaults.object(forKey: "MacSendOnHitFullCharge") == nil {
macSendOnHitFullCharge = false
} else { macSendOnHitFullCharge = defaults.bool(forKey: "MacSendOnHitFullCharge") }
}
.onDisappear() {
defaults.set(macSendOnPluggedIn, forKey: "MacSendOnPluggedIn")
defaults.set(macSendOnUnplugged, forKey: "MacSendOnUnplugged")
defaults.set(macSendOnHitFullCharge, forKey: "MacSendOnHitFullCharge")
}.onChange(of: macSendSettings) {_ in
defaults.set(macSendOnPluggedIn, forKey: "MacSendOnPluggedIn")
defaults.set(macSendOnUnplugged, forKey: "MacSendOnUnplugged")
defaults.set(macSendOnHitFullCharge, forKey: "MacSendOnHitFullCharge")
}
#elseif os(watchOS)
AutomationsViewNotEligibleView()
#elseif os(visionOS)
AutomationsViewNotEligibleView()
#elseif os(tvOS)
AutomationsViewNotEligibleView()
#elseif os(iOS)
AutomationsViewRequiresShortcutsView()
#endif
}
.navigationTitle("Automations")
.onAppear() {
}

}
}

struct AutomationsViewNotEligibleView: View {

var body: some View {
VStack {
Form {
VStack{
Image(systemName: "exclamationmark.triangle").foregroundStyle(.red).font(.system(size: 30)).padding()
Text("This device does not support automations.")
}.multilineTextAlignment(.center)
}
}
.onAppear() {
}

}
}

struct AutomationsViewRequiresShortcutsView: View {

var body: some View {
VStack {
Form {
VStack{
Image(systemName: "info.circle").foregroundStyle(.yellow).font(.system(size: 30)).padding()
Text("This device does not support in-app automations, but it can use Shortcuts Automations.\n\nPlease see the Help for more details about Shortcuts Automations.")
}
}
}
.onAppear() {
}

}
}

struct AutomationsView_Previews: PreviewProvider {
static var previews: some View {
AutomationsView()
}
}
7 changes: 7 additions & 0 deletions dcbattwebhook-swift/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ struct ContentView: View {
.tabItem {
Label("Settings", systemImage: "gearshape")
}
AutomationsView()
.tabItem {
Label("Automations", systemImage: "gearshape.2")
}
HelpView()
.tabItem {
Label("Help", systemImage: "questionmark.circle")
Expand All @@ -47,6 +51,9 @@ struct ContentView: View {
NavigationLink(destination: SettingsView(), isActive: $isShowingSettings, label: {
Label("Settings", systemImage: "gearshape")
})
NavigationLink{AutomationsView()} label: {
Label("Automations", systemImage: "gearshape.2")
}
NavigationLink(destination: HelpView(), isActive: $isShowingHelp, label: {
Label("Help", systemImage: "questionmark.circle")
})
Expand Down
8 changes: 8 additions & 0 deletions dcbattwebhook-swift/DefaultsUsed.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
- SelectedServiceType
- String, stores the user's preference on which service (discord, telegram, etc) they want to use. Must be selected from a predefined list. See serviceTypes array in WebhookSettingsView.swift

### Automations
- MacSendOnPluggedIn
- Bool, stores whether the user has chosen to send battery info when plugging in their Mac (false by default)
- MacSendOnUnplugged
- Bool, stores whether the user has chosen to send battery info when unolugging their Mac (false by default)
- MacSendOnHitFullCharge
- Bool, stores whether the user has chosen to send battery info when their Mac hits 100% charge (false by default as not yet implemented)

### Service Specific Settings
The way these are constructed is as follows:
```swift
Expand Down
5 changes: 4 additions & 1 deletion dcbattwebhook-swift/MacSupport/MacAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let source = IOPSNotificationCreateRunLoopSource(
{
_ in
print(getPowerSource())
if macAutomationsSavedPowerSource != GetMacPowerSource() {
HandleMacPowerStateChange()
macAutomationsSavedPowerSource = GetMacPowerSource()
}

},nil).takeRetainedValue()

Expand Down
69 changes: 68 additions & 1 deletion dcbattwebhook-swift/MacSupport/MacIOKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,51 @@ import Foundation
import IOKit
import IOKit.ps

func HandleMacPowerStateChange() {
switch GetMacPowerSource() {
case "Battery Power":
if (GetMacAutomationSetting(automationSetting: "MacSendOnUnplugged")) {
let isSettingsValid = ValidateSettings()

if (isSettingsValid.err == true) {
// config error
}
else {
let ResultsVar = sendInfo(isCurrentlyCharging: false, didGetPluggedIn: false, didGetUnplugged: true, didHitFullCharge: false)
SaveAutomationCurrentDate()
if (ResultsVar.err) {
// network error
}
else {
// success
}

}
}
case "AC Power":
if (GetMacAutomationSetting(automationSetting: "MacSendOnPluggedIn")) {
let isSettingsValid = ValidateSettings()

if (isSettingsValid.err == true) {
// config error
}
else {
let ResultsVar = sendInfo(isCurrentlyCharging: false, didGetPluggedIn: true, didGetUnplugged: false, didHitFullCharge: false)
SaveAutomationCurrentDate()
if (ResultsVar.err) {
// network error
}
else {
// success
}

}
}
default:
print("Unknown")
}
}

/**
Returns the current power source as string.

Expand All @@ -18,7 +63,7 @@ import IOKit.ps

- Warning: Only available on macOS
*/
func getPowerSource() -> String {
func GetMacPowerSource() -> String {
guard let snapshot = IOPSCopyPowerSourcesInfo()?.takeRetainedValue() else { return "Unknown" }
guard let sources: NSArray = IOPSCopyPowerSourcesList(snapshot)?.takeRetainedValue() else { return "Unknown" }
for ps in sources {
Expand All @@ -31,4 +76,26 @@ func getPowerSource() -> String {

return "Unknown"
}

/**
Returns whether or not the Mac is charging

- Returns:
it's a bool

- Warning: Only available on macOS
*/
func GetMacIsCharging() -> Bool {
guard let snapshot = IOPSCopyPowerSourcesInfo()?.takeRetainedValue() else { return false }
guard let sources: NSArray = IOPSCopyPowerSourcesList(snapshot)?.takeRetainedValue() else { return false }
for ps in sources {
guard let info: NSDictionary = IOPSGetPowerSourceDescription(snapshot, ps as CFTypeRef)?.takeUnretainedValue() else { return false }

if let isCharging = info[kIOPSIsChargingKey] as? Bool {
return isCharging
}
}

return false
}
#endif
58 changes: 58 additions & 0 deletions dcbattwebhook-swift/MacSupport/MacSettingsUtils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// MacSettingsUtils.swift
// Battery Webhook
//
// Created by Stella Luna on 11/21/23.
//

#if os(macOS)
import Foundation

/**
Gets a Mac Automation Setting and returns it

- Parameters:
- automationSetting: a valid setting to retrieve as described below

A valid setting is one of the following strings:
```
MacSendOnPluggedIn
MacSendOnUnplugged
MacSendOnHitFullCharge
```

- Returns:
Value of the Mac Automation Setting as Bool

- Warning: Only available on macOS, and returns `false` if an invalid setting is specified
*/
func GetMacAutomationSetting(automationSetting: String) -> Bool {
let defaults = UserDefaults.standard

var macSendOnPluggedIn = false
var macSendOnUnplugged = false
var macSendOnHitFullCharge = false

if defaults.object(forKey: "MacSendOnPluggedIn") == nil {
macSendOnPluggedIn = false
} else { macSendOnPluggedIn = defaults.bool(forKey: "MacSendOnPluggedIn") }
if defaults.object(forKey: "MacSendOnUnplugged") == nil {
macSendOnUnplugged = false
} else { macSendOnUnplugged = defaults.bool(forKey: "MacSendOnUnplugged") }
if defaults.object(forKey: "MacSendOnHitFullCharge") == nil {
macSendOnHitFullCharge = false
} else { macSendOnHitFullCharge = defaults.bool(forKey: "MacSendOnHitFullCharge") }

switch automationSetting {
case "MacSendOnPluggedIn":
return macSendOnPluggedIn
case "MacSendOnUnplugged":
return macSendOnUnplugged
case "MacSendOnHitFullCharge":
return macSendOnHitFullCharge
default:
return false
}
}

#endif
File renamed without changes.
3 changes: 2 additions & 1 deletion dcbattwebhook-swift/dcbattwebhook_swiftApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ public let prodName = "Battery Webhook"
/// Base version of the app, use `version` if you want the running OS as well
let versionNum = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown"
let versionBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "unknown"
let versionType = "dev"
let versionType = "RC"
public let versionBase = "\(versionNum)(\(versionBuild)) \(versionType)"

#if os(macOS)
public var macAutomationsSavedPowerSource = GetMacPowerSource()
public let version = "\(versionBase) on macOS"
#elseif os(watchOS)
public let version = "\(versionBase) on watchOS"
Expand Down

0 comments on commit 5764d7e

Please sign in to comment.