feat: add CLI with Cobra and timestamped logging
Introduce structured CLI argument parsing using Cobra with three configurable flags: --vendor: USB vendor ID (default: 0x0fcf) --product: USB product ID (default: 0x1008) --device: ANT+ device number (default: 3001) Add timestamped logging for all log statements with format YYYY-MM-DD HH:MM:SS. Log vendor, product, and device values at startup in both hex and decimal. Remove hardcoded USB identifiers from main.go and consolidate all startup logic into cli.go with a dedicated Config struct. Update ant.go ListenForTrainer signature to accept uint16 for vendor and product IDs.
This commit is contained in:
+2
-2
@@ -26,9 +26,9 @@ func resetAndWait(drv io.ReadWriter) error {
|
||||
}
|
||||
}
|
||||
|
||||
func ListenForTrainer(vendorId gousb.ID, productId gousb.ID, deviceNumber uint32,
|
||||
func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32,
|
||||
events chan<- Event) {
|
||||
drv, err := usb.GetDevice(vendorId, productId)
|
||||
drv, err := usb.GetDevice(gousb.ID(vendorId), gousb.ID(productId))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Version and BuildTime are injected via ldflags at build time.
|
||||
var Version = "unknown"
|
||||
var BuildTime = "unknown"
|
||||
|
||||
func parseHex(s string, bits int) (uint64, error) {
|
||||
return strconv.ParseUint(s, 0, bits)
|
||||
}
|
||||
|
||||
type config struct {
|
||||
vendor string
|
||||
product string
|
||||
device string
|
||||
vendorID uint64
|
||||
productID uint64
|
||||
deviceNumber uint32
|
||||
}
|
||||
|
||||
func newConfig() *config {
|
||||
return &config{}
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "bridge",
|
||||
Short: "ANT+ to Bluetooth LE bridge",
|
||||
Long: `Bridge an ANT+ cycling power trainer over Bluetooth Low Energy.
|
||||
This allows these trainers to be used with modern devices that don't support ANT+.`,
|
||||
Version: fmt.Sprintf("%s (built %s)", Version, BuildTime),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
c := newConfig()
|
||||
|
||||
c.vendor = cmd.Flags().Lookup("vendor").Value.String()
|
||||
c.product = cmd.Flags().Lookup("product").Value.String()
|
||||
c.device = cmd.Flags().Lookup("device").Value.String()
|
||||
|
||||
vendorID, err := parseHex(c.vendor, 16)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid --vendor %q: %w", c.vendor, err)
|
||||
}
|
||||
productID, err := parseHex(c.product, 16)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid --product %q: %w", c.product, err)
|
||||
}
|
||||
deviceNumber, err := parseHex(c.device, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid --device %q: %w", c.device, err)
|
||||
}
|
||||
|
||||
c.vendorID = vendorID
|
||||
c.productID = productID
|
||||
c.deviceNumber = uint32(deviceNumber)
|
||||
|
||||
logMsgf("ANT Bridge %s built %s", Version, BuildTime)
|
||||
logMsgf("vendor: 0x%04x/%d", vendorID, vendorID)
|
||||
logMsgf("product: 0x%04x/%d", productID, productID)
|
||||
logMsgf("device: 0x%03x/%d", deviceNumber, deviceNumber)
|
||||
|
||||
exec.Command("hciconfig", "hci0", "down").Run()
|
||||
exec.Command("hciconfig", "hci0", "up").Run()
|
||||
|
||||
events := make(chan Event)
|
||||
go ListenForTrainer(uint16(c.vendorID), uint16(c.productID), c.deviceNumber, events)
|
||||
ExposeBluetooth(events)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.Flags().String("vendor", "0x0fcf", "USB vendor ID (hex, e.g. 0x0fcf)")
|
||||
rootCmd.Flags().String("product", "0x1008", "USB product ID (hex, e.g. 0x1008)")
|
||||
rootCmd.Flags().String("device", "3001", "ANT+ device number (hex or decimal, e.g. 3001 or 0xbb9)")
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func logMsg(msg string) {
|
||||
fmt.Println(time.Now().Format("2006-01-02 15:04:05") + " " + msg)
|
||||
}
|
||||
|
||||
func logPrintf(format string, a ...any) {
|
||||
fmt.Printf(time.Now().Format("2006-01-02 15:04:05")+" "+format, a...)
|
||||
}
|
||||
|
||||
func logMsgf(format string, a ...any) {
|
||||
logPrintf(format+"\n", a...)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/google/gousb"
|
||||
)
|
||||
|
||||
func main() {
|
||||
exec.Command("hciconfig", "hci0", "down").Run()
|
||||
exec.Command("hciconfig", "hci0", "up").Run()
|
||||
|
||||
var vendorId gousb.ID = 0x0fcf
|
||||
var productId gousb.ID = 0x1008
|
||||
var deviceNumber uint32 = 3001
|
||||
|
||||
events := make(chan Event)
|
||||
go ListenForTrainer(vendorId, productId, deviceNumber, events)
|
||||
ExposeBluetooth(events)
|
||||
}
|
||||
Reference in New Issue
Block a user