🎯 Motivasi
Saya cari fitur untuk membatasi pengisian baterai di MacBook ke 80% biar awet...
dan ketemulah AIDente Pro — fitur bagus tapi yaa... IDR 200K+
😅
Karena gak ada opsi bawaan macOS, akhirnya bikin:
RyBatteryNotifier – macOS menu bar app untuk pantau status baterai, kasih notifikasi saat sudah penuh.
🛠️ Tools yang Digunakan
-
Swift
+SwiftUI
-
IOKit.ps
– untuk baca status baterai -
UserNotifications
– buat kirim notifikasi -
NSStatusBar
– tampil di menu bar - ✨ Optional:
LaunchAgent
untuk auto-start
Fitur Utama
✅ Tampilkan status baterai real-time
✅ Menu bar dengan ikon baterai
✅ Bisa atur limit pengisian (misal 80%)
✅ Notifikasi otomatis saat limit tercapai
✅ Mode Auto Discharge
(simulasi nonaktif charging) masih on progress
✅ Tombol menu untuk "Show App", "Discharge", dan "Quit" di menu bar.
Membuat Proyek Xcode
- Buka Xcode →
New Project
→App
- Pilih SwiftUI dan platform macOS
- Nama project:
RyBatteryNotifier
🧠 Cek Status Baterai via IOKit
Buat file BatteryService.swift
:
import IOKit.ps import UserNotifications import Foundation class BatteryService: ObservableObject { @Published var batteryPercentage: Int = 0 @Published var isCharging: Bool = false @Published var isInDischargeMode: Bool = false @Published var autoDischargeEnabled: Bool = UserDefaults.standard.bool(forKey: "autoDischargeEnabled") init() { Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in self.updateBatteryInfo() } updateBatteryInfo() } func updateBatteryInfo() { guard let snapshot = IOPSCopyPowerSourcesInfo()?.takeRetainedValue(), let sources = IOPSCopyPowerSourcesList(snapshot)?.takeRetainedValue() as? [CFTypeRef] else { return } for ps in sources { if let info = IOPSGetPowerSourceDescription(snapshot, ps)?.takeUnretainedValue() as? [String: Any], let isCharging = info[kIOPSIsChargingKey as String] as? Bool, let current = info[kIOPSCurrentCapacityKey as String] as? Int, let max = info[kIOPSMaxCapacityKey as String] as? Int { let percent = Int((Double(current) / Double(max)) * 100) DispatchQueue.main.async { self.batteryPercentage = percent self.isCharging = isCharging } let limit = UserDefaults.standard.integer(forKey: "maxLimit") if autoDischargeEnabled && isCharging && percent >= limit { DispatchQueue.main.async { self.isInDischargeMode = true } self.sendDischargePrompt() return } if isCharging && percent >= limit { self.sendNotification() } } } } func sendNotification() { let content = UNMutableNotificationContent() content.title = "⚡ Baterai Penuh" content.body = "Baterai sudah mencapai limit. Cabut charger ya~" content.sound = .default UNUserNotificationCenter.current().add(UNNotificationRequest( identifier: UUID().uuidString, content: content, trigger: nil )) } func sendDischargePrompt() { let content = UNMutableNotificationContent() content.title = "🔌 Mode Discharge Aktif" content.body = "Auto Discharge aktif. Cabut charger manual ya~" content.sound = .default UNUserNotificationCenter.current().add(UNNotificationRequest( identifier: UUID().uuidString, content: content, trigger: nil )) } }
Menu Bar App (Agent Style)
Tambahkan StatusBarController.swift
untuk icon di taskbar:
import AppKit class StatusBarController { private var statusItem: NSStatusItem! init(showApp: @escaping () -> Void, discharge: @escaping () -> Void) { statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) statusItem.button?.image = NSImage(systemSymbolName: "battery.100", accessibilityDescription: "RyBatteryNotifier") let menu = NSMenu() let dischargeItem = NSMenuItem(title: "Discharge Mode", action: #selector(doDischarge), keyEquivalent: "d") dischargeItem.target = self let showAppItem = NSMenuItem(title: "Show App", action: #selector(doShowApp), keyEquivalent: "s") showAppItem.target = self let quitItem = NSMenuItem(title: "Quit RyBatteryNotifier", action: #selector(doQuit), keyEquivalent: "q") quitItem.target = self menu.addItem(dischargeItem) menu.addItem(showAppItem) menu.addItem(.separator()) menu.addItem(quitItem) statusItem.menu = menu self.onShowApp = showApp self.onDischarge = discharge } private var onShowApp: (() -> Void)? private var onDischarge: (() -> Void)? @objc func doShowApp() { onShowApp?() } @objc func doDischarge() { onDischarge?() } @objc func doQuit() { NSApp.terminate(nil) } }
Info.plist Tweaks
Tambahkan ke Info.plist
agar app jadi menu bar agent (tanpa dock icon):
<key>LSUIElement</key> <true/> <key>LSApplicationCategoryType</key> <string>public.app-category.utilities</string>
Install Manual (Karena Gratis Account)
Xcode personal account tidak bisa Archive-distribute, jadi:
cd ~/Library/Developer/Xcode/DerivedData open .
Masuk ke folder project → Build/Products/Debug/
Temukan RyBatteryNotifier.app
dan copy ke /Applications/:
cp -R RyBatteryNotifier.app /Applications/
Jalankan:
open /Applications/RyBatteryNotifier.app
🔁 Auto Start Saat Login (Optional)
- Buat file
~/Library/LaunchAgents/com.ryfazrin.battery.plist
<plist version="1.0"> <dict> <key>Label</key> <string>com.ryfazrin.battery</string> <key>ProgramArguments</key> <array> <string>/Applications/RyBatteryNotifier.app/Contents/MacOS/RyBatteryNotifier</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>
- Jalankan:
launchctl load ~/Library/LaunchAgents/com.ryfazrin.battery.plist
Penutup
Kini saya punya macOS app buatan sendiri di menu bar dan bisa bantu rawat baterai MacBook.
💸 Gratis, open-source, dan fun to build.
✨ Gak perlu AIDente Pro, cukup semangat dan Xcode 😄
Source code?
Repo GitHub ada di komentar~ 🧪
Top comments (1)
Repo: github.com/ryfazrin/RyBatteryNotifier