Skip to content

allow sharing bluetooth by either remove HCIDEVDOWN ioctl or (better) switch to a BlueZ D-Bus based Go library (e.g., muka/go-bluetooth) #158

Closed

Description

@AloisKlingler

When TeslaBleHttpProxy runs on a host with other BLE services (e.g., a door lock using bleak/BlueZ), the raw HCI socket usage and HCIDEVDOWN calls make the adapter invisible to BlueZ D-Bus. Other BLE apps get 'No Bluetooth adapters found' until the Tesla proxy is stopped and bluetooth service restarted.

there would be two ways to avoid:

  1. Switch from go-ble/ble to a BlueZ D-Bus based Go library (e.g., muka/go-bluetooth (https://github.com/muka/go-bluetooth) or tinygo-org/bluetooth (https://github.com/tinygo-org/bluetooth)) — this would let the proxy coexist with other BLE applications on the same host.

  2. Or at minimum: stop calling HCIDEVDOWN — the fork wimaha/ble_BleConnectFix is where that happens. Removing the HCIDEVDOWN ioctl and instead using a softer reset would at least stop it from stealing the adapter from BlueZ.

The real blocker is HCI_CHANNEL_USER on line ~124. That's the binding mode that gives exclusive access. The down/up ioctls are just preparation for it.
The problem is: HCI_CHANNEL_USER requires the adapter to be down and requires exclusive access. There's no way to use that channel and share with BlueZ. It's by design in the Linux kernel.

So the realistic options to suggest (without switching to BlueZ D-Bus) are:

  1. Use HCI_CHANNEL_RAW instead of HCI_CHANNEL_USER — this channel doesn't require exclusive access and coexists with BlueZ. The downside: it's more limited and the go-ble library would need significant rework to handle the different semantics.
  2. Release the socket when idle — currently the connection opens/closes per command, but if the socket stays bound between commands, the adapter stays locked. If they ensure Close() fully unbinds the HCI_CHANNEL_USER socket after each operation, BlueZ would re-acquire the adapter in between. This is the smallest change — just make sure the HCI socket is opened on-demand and fully closed (including fd close) after each Tesla command completes.

The Close() in socket.go closes the file descriptor but never calls HCIDEVUP to bring the adapter back. After the Tesla proxy finishes a command and closes the socket, the adapter stays down. BlueZ never sees it again until something externally runs hciconfig hci0 up or hciDevUp.

The smallest fix: add one ioctl call in Close():

  func (s *Socket) Close() error {
      close(s.closed)
      s.Write([]byte{0x01, 0x09, 0x10, 0x00})
      s.rmu.Lock()
      defer s.rmu.Unlock()
      err := unix.Close(s.fd)

      // Bring adapter back up so BlueZ can re-acquire it
      fd, _ := unix.Socket(unix.AF_BLUETOOTH, unix.SOCK_RAW, unix.BTPROTO_HCI)
      if fd >= 0 {
          ioctl(uintptr(fd), hciUpDevice, uintptr(s.id))  // would need to store id
          unix.Close(fd)
      }

      return errors.Wrap(err, "can't close hci socket")
  }

This way after every Tesla BLE operation, the adapter comes back up and BlueZ re-registers it. Other BLE apps would work again between Tesla commands without any restarts.

Activity

  1. Metatron2k2 commented on May 24, 2026

    @Metatron2k2

    You can try to use my fork:
    https://github.com/Metatron2k2/TeslaBleHttpProxy/tree/feature/tinygo-zlymeda-ghcr-build
    It's tinygo based. There is also a docker image you can pull.

  2. AloisKlingler commented on Aug 3, 2026

    @AloisKlingler
    ContributorAuthor

    @wimaha 's version with this two fixes works now since a few days without issues. would be great if you can look at the PRs and merge them.

    @Metatron2k2 your version was not stable at all for me, lost connection & was unable to reconnect afterwards, was unresponsive. but thanks for pointing out.

  3. wimaha commented on Oct 18, 2026

    @wimaha
    Owner

    @AloisKlingler Thanks for bringing this up. Can you test the newest version on :dev channel or compile from main? I've implemented a solution based on your PR. See: #174

    You can set releaseAdapterWhenIdle and connectionTimeout in this version.

  4. AloisKlingler commented on Oct 19, 2026

    @AloisKlingler
    ContributorAuthor

    @wimaha I have :dev running since today morning but could not yet have a look at the addtional possibilities for settings. I would need some more time to verify, at least a week, to tell you after fiddling around if it got stable.

    docker-compose.yml:

        environment:
          - cacheMaxAge=30
          - connectionTimeout=10
          - ReleaseAdapterWhenIdle=true
    

    log shows:

    2026/10/19 16:04:59 INFO Env: httpListenAddress=:8080
    2026/10/19 16:04:59 INFO Env: cacheMaxAge=30
    2026/10/19 16:04:59 INFO Env: scanTimeout=5
    2026/10/19 16:04:59 INFO Env: vehicleDataCacheTime=30
    2026/10/19 16:04:59 INFO Env: connectionTimeout=10
    2026/10/19 16:04:59 INFO Env: releaseAdapterWhenIdle=false
    

    I have tried "true", "1" it always logs to "false"

  5. wimaha commented on Oct 19, 2026

    @wimaha
    Owner

    'releaseAdapterWhenIdle' must have lower case first letter. In your .yml you have upper case first letter.

  6. AloisKlingler commented on Oct 19, 2026

    @AloisKlingler
    ContributorAuthor

    9. 'releaseAdapterWhenIdle' must have lower case first letter. In your .yml you have upper case first letter.

    now it is loaded correct from config. I copied from the struct definition

    now I can see my smartlock implementation is aquiring BT successful again without stopping and restarting teslaBLEhttpproxy.
    so, running now with true and connect timeout=10, I am collecting statistics how often it needs to stop and restart teslablehttpproxy, will report back 👍

  7. AloisKlingler commented on Oct 22, 2026

    @AloisKlingler
    ContributorAuthor

    worked the last days flawless as my own version.
    means: 99 retries (where teslablehttpproxy used BT adapter), 36 of them have not yet been released (10sec timeout was not reached), all other cases the adapter was already released and therefore worked after a simple BlueZ clearing and retry.

    updated now from :dev to :latest
    thank you for merging 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions