package main import ( "bufio" "flag" "fmt" "log" "os" "strconv" "strings" "time" "github.com/getlantern/systray" "zmk-battman/assets" "zmk-battman/internal" ) var ( setup = flag.Bool("setup", false, "Run setup") config = flag.Bool("config", false, "Show config") interval = flag.Int("interval", 0, "Update interval (5-300 seconds)") device = flag.String("device", "", "Device address") threshold = flag.Int("threshold", 0, "Low battery threshold (5-50%)") // NEW notify = flag.String("notify", "", "Enable/disable notifications (true/false)") // NEW ) func main() { flag.Parse() if *config { showConfig() return } if *setup { runSetup() return } if *interval > 0 || *device != "" || *threshold > 0 || *notify != "" { updateConfig() return } systray.Run(onReady, onExit) } func showConfig() { cm, err := internal.NewConfigManager() if err != nil { log.Fatal(err) } cm.Print() } func updateConfig() { cm, err := internal.NewConfigManager() if err != nil { log.Fatal(err) } if *interval > 0 { cm.SetInterval(*interval) fmt.Printf("Set update interval to %d seconds\n", *interval) } if *threshold > 0 { cm.SetLowBatteryThreshold(*threshold) fmt.Printf("Set low battery threshold to %d%%\n", *threshold) } if *notify != "" { enabled := strings.ToLower(*notify) == "true" cm.SetNotifications(enabled) fmt.Printf("Set notifications: %v\n", enabled) } if *device != "" { scanner, err := internal.NewScanner() if err != nil { log.Fatal(err) } defer scanner.Close() dev, _, err := scanner.FindDevice(*device) if err != nil || dev.Address == "" { log.Fatalf("Device %s not found", *device) } cm.SetDevice(dev.Name, dev.Address) fmt.Printf("Set device to %s (%s)\n", dev.Name, dev.Address) } if err := cm.Save(); err != nil { log.Fatal(err) } fmt.Println("Configuration updated!") } func runSetup() { fmt.Println("=== ZMK Battery Monitor Setup ===") cm, err := internal.NewConfigManager() if err != nil { log.Fatal(err) } if cm.IsConfigured() { fmt.Println("\nCurrent configuration:") cm.Print() fmt.Println("\nThis setup will override the existing configuration.") reader := bufio.NewReader(os.Stdin) fmt.Print("Continue? (Y/n): ") input, _ := reader.ReadString('\n') if strings.ToLower(strings.TrimSpace(input)) == "n" { fmt.Println("Setup cancelled.") return } } cm = &internal.ConfigManager{} if err := cm.InitWithDefaults(); err != nil { log.Fatal(err) } scanner, err := internal.NewScanner() if err != nil { log.Fatal(err) } defer scanner.Close() devices, err := scanner.GetDevices() if err != nil { log.Fatal(err) } if len(devices) == 0 { fmt.Println("No Bluetooth devices found!") return } fmt.Printf("\nFound %d devices:\n", len(devices)) for i, dev := range devices { fmt.Printf("%d. %s (%s)\n", i+1, dev.Name, dev.Address) } reader := bufio.NewReader(os.Stdin) fmt.Print("Select device (number): ") for { input, _ := reader.ReadString('\n') choice, err := strconv.Atoi(strings.TrimSpace(input)) if err != nil || choice < 1 || choice > len(devices) { fmt.Print("Invalid choice, try again: ") continue } selected := devices[choice-1] cm.SetDevice(selected.Name, selected.Address) fmt.Printf("✅ Selected: %s (%s)\n", selected.Name, selected.Address) break } fmt.Print("\nUpdate interval in seconds (default 30): ") input, _ := reader.ReadString('\n') input = strings.TrimSpace(input) if input != "" { if interval, err := strconv.Atoi(input); err == nil && interval >= 5 && interval <= 300 { cm.SetInterval(interval) fmt.Printf("✅ Set update interval to %d seconds\n", interval) } else { fmt.Println("Invalid interval, using default (30 seconds)") } } fmt.Print("Enable low battery notifications? (Y/n): ") input, _ = reader.ReadString('\n') enableNotify := strings.ToLower(strings.TrimSpace(input)) != "n" cm.SetNotifications(enableNotify) fmt.Printf("✅ Notifications: %v\n", enableNotify) if enableNotify { fmt.Print("Low battery threshold percentage (default 20): ") input, _ = reader.ReadString('\n') input = strings.TrimSpace(input) if input != "" { if thresh, err := strconv.Atoi(input); err == nil && thresh >= 5 && thresh <= 50 { cm.SetLowBatteryThreshold(thresh) fmt.Printf("✅ Set threshold to %d%%\n", thresh) } else { fmt.Println("Invalid threshold, using default (20%)") } } } if err := cm.Save(); err != nil { log.Fatal(err) } fmt.Println("\n✅ Setup complete!") fmt.Println("New configuration:") cm.Print() fmt.Println("\nRun without --setup to start monitoring.") } var app struct { config *internal.ConfigManager scanner *internal.Scanner monitor *internal.Monitor tray *internal.SysTray } func onReady() { var err error app.config, err = internal.NewConfigManager() if err != nil { log.Fatal(err) } if !app.config.IsConfigured() { fmt.Println("❌ ZMK Battery Monitor is not configured!") fmt.Println("") fmt.Println("Please run setup first:") fmt.Printf(" %s --setup\n", os.Args[0]) fmt.Println("") fmt.Println("Or use quick configuration:") fmt.Printf(" %s --device AA:BB:CC:DD:EE:FF\n", os.Args[0]) fmt.Printf(" %s --threshold 15 # Set low battery threshold\n", os.Args[0]) fmt.Printf(" %s --notify true # Enable notifications\n", os.Args[0]) fmt.Printf(" %s --config # View current configuration\n", os.Args[0]) systray.Quit() return } app.scanner, err = internal.NewScanner() if err != nil { log.Fatal(err) } cfg := app.config.Get() app.monitor, err = internal.NewMonitor(app.scanner, cfg) if err != nil { log.Fatal(err) } app.monitor.SetDevice(cfg.Name, cfg.Address) app.tray = internal.NewSysTray(assets.IconData, func() { systray.Quit() }) app.monitor.SetCallback(app.tray.Update) app.monitor.Start(time.Duration(cfg.UpdateInterval) * time.Second) } func onExit() { if app.monitor != nil { app.monitor.Stop() } if app.scanner != nil { app.scanner.Close() } }