Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import (

"github.com/spf13/cobra"
"tinygo.org/x/bluetooth"

"github.com/tzermias/deskctl/pkg/logger"
)

var address string
var verbose bool

var adapter *bluetooth.Adapter

Expand All @@ -22,13 +25,22 @@ var rootCmd = &cobra.Command{
Long: `Controls standing desks equipped with Jiecang controllers
Moves the desk up/down, manages memory presets`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// Set verbose mode based on flag
logger.SetVerbose(verbose)

if verbose {
logger.Printf("Verbose mode enabled")
}

// Initialize bluetooth adapter
adapter = bluetooth.DefaultAdapter
err := adapter.Enable()
if err != nil {
fmt.Fprintf(os.Stderr, "Could not enable Bluetooth adapter: %v\n", err)
os.Exit(1)
}

logger.Printf("Bluetooth adapter enabled successfully")
},
Run: func(cmd *cobra.Command, args []string) {
_ = cmd.Help()
Expand All @@ -47,4 +59,5 @@ func Execute(ctx context.Context) {

func init() {
rootCmd.PersistentFlags().StringVarP(&address, "address", "a", "", "Device address")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable verbose output")
}
21 changes: 12 additions & 9 deletions hack/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package main

import (
"context"
"log"
"time"

"github.com/tzermias/deskctl/pkg/jiecang"
"github.com/tzermias/deskctl/pkg/logger"
"tinygo.org/x/bluetooth"
)

Expand All @@ -14,6 +14,9 @@ const (
)

func main() {
// Enable verbose logging for test script
logger.SetVerbose(true)

adapter := bluetooth.DefaultAdapter

err := adapter.Enable()
Expand All @@ -22,18 +25,18 @@ func main() {
}

err = adapter.Scan(onScan)
log.Println("Scanning for Lierda devices ")
logger.Println("Scanning for Lierda devices ")
if err != nil {
panic("Failed to register scan callback")
}
}

func onScan(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {
if device.HasServiceUUID(bluetooth.New16BitUUID(LierdaDeviceID)) {
log.Println("Found Lierda device:", device.Address.String(), device.RSSI, device.LocalName())
logger.Println("Found Lierda device:", device.Address.String(), device.RSSI, device.LocalName())
j, err := jiecang.Init(adapter, device.Address)
if err != nil {
log.Printf("Failed to initialize device: %v\n", err)
logger.Printf("Failed to initialize device: %v\n", err)
return
}

Expand All @@ -43,22 +46,22 @@ func onScan(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {

// Go to Memory1
if err := j.GoToMemory(ctx, 1); err != nil {
log.Printf("Failed to go to memory1: %v\n", err)
logger.Printf("Failed to go to memory1: %v\n", err)
return
}
time.Sleep(5 * time.Second)
// Go to Memory2
if err := j.GoToMemory(ctx, 2); err != nil {
log.Printf("Failed to go to memory2: %v\n", err)
logger.Printf("Failed to go to memory2: %v\n", err)
return
}
time.Sleep(200 * time.Millisecond)

log.Println("Disconnecting...")
logger.Println("Disconnecting...")
if err := j.Disconnect(); err != nil {
log.Printf("Error when disconnecting: %v\n", err)
logger.Printf("Error when disconnecting: %v\n", err)
return
}
log.Println("Disconnected...")
logger.Println("Disconnected...")
}
}
9 changes: 5 additions & 4 deletions pkg/jiecang/jiecang.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ package jiecang
import (
"bytes"
"fmt"
"log"
"sync"

"tinygo.org/x/bluetooth"

"github.com/tzermias/deskctl/pkg/logger"
)

var commands = map[string][]byte{
Expand Down Expand Up @@ -171,7 +172,7 @@ func Init(a *bluetooth.Adapter, addr bluetooth.Address) (*Jiecang, error) {
if err != nil {
return nil, fmt.Errorf("failed to read initial height: %w", err)
}
log.Printf("Initial height: %d mm", j.currentHeight)
logger.Printf("Initial height: %d mm", j.currentHeight)

//Fetch height memory presets
j.presets = make(map[string]uint8)
Expand Down Expand Up @@ -283,10 +284,10 @@ func (j *Jiecang) characteristicReceiver(buf []byte) {
j.AntiCollisionSensitivity = uint8(msg[i][3])
j.mu.Unlock()
default: // Any other case
log.Printf("Received: %x", msg[i])
logger.Printf("Received: %x", msg[i])
}
} else {
log.Printf("Received: %x", msg[i])
logger.Printf("Received: %x", msg[i])
}
}
}
5 changes: 3 additions & 2 deletions pkg/jiecang/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package jiecang
import (
"context"
"fmt"
"log"
"math"
"time"

"github.com/tzermias/deskctl/pkg/logger"
)

// GoToMemory moves the desk to the specified memory preset (1-3).
Expand Down Expand Up @@ -127,7 +128,7 @@ func (j *Jiecang) SaveMemory(memoryNum int) error {
return fmt.Errorf("failed to save memory%d: %w", memoryNum, err)
}

log.Printf("Saved height %d cm to memory %d", j.currentHeight, memoryNum)
logger.Printf("Saved height %d cm to memory %d", j.currentHeight, memoryNum)
time.Sleep(200 * time.Millisecond)
return nil
}
Expand Down
71 changes: 71 additions & 0 deletions pkg/logger/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package logger

import (
"io"
"log"
"os"
"sync"
)

var (
mu sync.RWMutex
verbose bool
logger *log.Logger
)

func init() {
logger = log.New(os.Stdout, "", log.LstdFlags)
}

// SetVerbose enables or disables verbose logging
func SetVerbose(v bool) {
mu.Lock()
defer mu.Unlock()
verbose = v
}

// IsVerbose returns whether verbose logging is enabled
func IsVerbose() bool {
mu.RLock()
defer mu.RUnlock()
return verbose
}

// SetOutput sets the output destination for the logger
func SetOutput(w io.Writer) {
mu.Lock()
defer mu.Unlock()
logger.SetOutput(w)
}

// Printf prints a log message only if verbose mode is enabled
func Printf(format string, v ...interface{}) {
mu.RLock()
defer mu.RUnlock()
if verbose {
logger.Printf(format, v...)
}
}

// Println prints a log message only if verbose mode is enabled
func Println(v ...interface{}) {
mu.RLock()
defer mu.RUnlock()
if verbose {
logger.Println(v...)
}
}

// Error always prints error messages regardless of verbose mode
func Error(v ...interface{}) {
mu.RLock()
defer mu.RUnlock()
logger.Println(v...)
}

// Errorf always prints error messages regardless of verbose mode
func Errorf(format string, v ...interface{}) {
mu.RLock()
defer mu.RUnlock()
logger.Printf(format, v...)
}
104 changes: 104 additions & 0 deletions pkg/logger/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package logger

import (
"bytes"
"strings"
"testing"
)

func TestVerboseToggle(t *testing.T) {
buf := &bytes.Buffer{}
SetOutput(buf)

// Test verbose disabled (default)
SetVerbose(false)
Printf("test message")
if buf.Len() > 0 {
t.Error("Expected no output when verbose is disabled")
}

// Test verbose enabled
buf.Reset()
SetVerbose(true)
Printf("test message")
if !strings.Contains(buf.String(), "test message") {
t.Error("Expected output when verbose is enabled")
}
}

func TestPrintln(t *testing.T) {
buf := &bytes.Buffer{}
SetOutput(buf)

// Test verbose disabled
SetVerbose(false)
Println("test message")
if buf.Len() > 0 {
t.Error("Expected no output when verbose is disabled")
}

// Test verbose enabled
buf.Reset()
SetVerbose(true)
Println("test message")
if !strings.Contains(buf.String(), "test message") {
t.Error("Expected output when verbose is enabled")
}
}

func TestErrorAlwaysPrints(t *testing.T) {
buf := &bytes.Buffer{}
SetOutput(buf)
SetVerbose(false)

// Test Error
Error("error message")
if !strings.Contains(buf.String(), "error message") {
t.Error("Expected error to print even when verbose is disabled")
}

// Test Errorf
buf.Reset()
Errorf("error %s", "formatted")
if !strings.Contains(buf.String(), "error formatted") {
t.Error("Expected formatted error to print even when verbose is disabled")
}
}

func TestIsVerbose(t *testing.T) {
SetVerbose(false)
if IsVerbose() {
t.Error("Expected IsVerbose to return false")
}

SetVerbose(true)
if !IsVerbose() {
t.Error("Expected IsVerbose to return true")
}
}

func TestConcurrentAccess(t *testing.T) {
buf := &bytes.Buffer{}
SetOutput(buf)
SetVerbose(true)

// Test concurrent reads and writes
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
Printf("concurrent message")
done <- true
}()
}

// Wait for all goroutines
for i := 0; i < 10; i++ {
<-done
}

// Should have 10 messages
count := strings.Count(buf.String(), "concurrent message")
if count != 10 {
t.Errorf("Expected 10 messages, got %d", count)
}
}
Loading