From 860b5b974d0ee0701d0625f58b432d7950ced485 Mon Sep 17 00:00:00 2001 From: AloisKlingler <11095760+AloisKlingler@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:28:48 +0200 Subject: [PATCH] Release BLE adapter when idle and make connection hold configurable Loop() now calls ble.CloseAdapter() when no command is pending, so the HCI adapter is handed back to BlueZ between command sessions and other BlueZ-based tools can share the same controller instead of being locked out until the proxy restarts. The post-command connection hold (previously hard-coded to 29s) is now configurable via the connectionTimeout environment variable (default 10s), matching the existing cacheMaxAge / scanTimeout options. A shorter hold frees the adapter for other tools sooner; a longer hold batches follow-up commands. --- config/config.go | 13 +++++++++++++ internal/ble/control/control.go | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 1186f44..b66d185 100644 --- a/config/config.go +++ b/config/config.go @@ -92,6 +92,7 @@ type Config struct { ScanTimeout int // Seconds to scan for BLE devices CacheMaxAge int // Seconds for HTTP Cache-Control header max-age (used for body controller state responses). If set to 0, cache headers are disabled. VehicleDataCacheTime int // Seconds to cache VehicleData endpoint responses in memory. Each endpoint is cached separately per VIN. + ConnectionTimeout int // Seconds to keep the BLE connection open after a command before releasing the adapter to BlueZ. Lower = share adapter sooner; higher = batch follow-up commands. } var AppConfig *Config @@ -145,12 +146,24 @@ func LoadConfig() *Config { } logging.Info("Env:", "vehicleDataCacheTime", vehicleDataCacheTimeInt) + connectionTimeout := os.Getenv("connectionTimeout") + if connectionTimeout == "" { + connectionTimeout = "10" // default value: 10 seconds + } + connectionTimeoutInt, err := strconv.Atoi(connectionTimeout) + if err != nil { + logging.Error("Invalid connectionTimeout value, using default (10)", "error", err) + connectionTimeoutInt = 10 + } + logging.Info("Env:", "connectionTimeout", connectionTimeoutInt) + return &Config{ LogLevel: envLogLevel, HttpListenAddress: addr, CacheMaxAge: cacheMaxAgeInt, ScanTimeout: scanTimeoutInt, VehicleDataCacheTime: vehicleDataCacheTimeInt, + ConnectionTimeout: connectionTimeoutInt, } } diff --git a/internal/ble/control/control.go b/internal/ble/control/control.go index 577bf68..5bd8745 100644 --- a/internal/ble/control/control.go +++ b/internal/ble/control/control.go @@ -75,6 +75,11 @@ func (bc *BleControl) Loop() { logging.Info("Retrying command", "Command", retryCommand.Command, "Body", retryCommand.Body) retryCommand = bc.connectToVehicleAndOperateConnection(retryCommand) } else { + // No pending retry: release the BLE adapter back to BlueZ while idle + // so BlueZ D-Bus and other tools can use it between command sessions. + if err := ble.CloseAdapter(); err != nil { + logging.Debug("Failed to release BLE adapter", "error", err) + } logging.Debug("Waiting for next command ...") // Wait for the next command select { @@ -403,7 +408,7 @@ func (bc *BleControl) TryConnectToVehicle(ctx context.Context, firstCommand *com func (bc *BleControl) operateConnection(car *vehicle.Vehicle, firstCommand *commands.Command) *commands.Command { logging.Debug("Operating connection ...") //defer log.Debug("operating connection done") - connectionCtx, cancel := context.WithTimeout(context.Background(), 29*time.Second) + connectionCtx, cancel := context.WithTimeout(context.Background(), time.Duration(config.AppConfig.ConnectionTimeout)*time.Second) defer cancel() cmd, err, _ := bc.ExecuteCommand(car, firstCommand, connectionCtx)