Feasability of using UniFi APs a bluetooth bridges

This is not yet a complete solution, but I’d like to share what I found out so far.

Ubiquiti uses a similar concept as ESPHome to bridge bluetooth devices to the Unifi-Protect service. Right now they only use it for the UP-Sense product, but the access points are certainly able to talk to any other bluetooth device.

The best solution would be to use interface (1), as it is available on an external TCP port of the access points. It’s fairly well protected (websocket with mTLS 1.3), so we can’t simply use tcpdump to listen to the traffic even though we have access to the TLS certificates and private keys for client and server. Another approach is to debug the unifi-protect application which is written in javascript and runs withing nodejs. The source is minified of course, so this is also not a trivial task.

The protocol that is used on top of the websocket seems to be called UCP4, as we can see from the library names on the access point.

ldd /usr/sbin/blebr2d
/lib/ld-musl-aarch64.so.1 (0x7fa74a1000)
libsodium.so.23 => /usr/lib/libsodium.so.23 (0x7fa7466000)
libssl.so.1.1 => /usr/lib/libssl.so.1.1 (0x7fa73fb000)
libcrypto.so.1.1 => /usr/lib/libcrypto.so.1.1 (0x7fa7294000)
libucp4cpp.so.0.1 => /usr/lib/libucp4cpp.so.0.1 (0x7fa7206000)
libbinmecpp.so.0 => /usr/lib/libbinmecpp.so.0 (0x7fa71e7000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x7fa709a000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x7fa7080000)
libc.so => /lib/ld-musl-aarch64.so.1 (0x7fa74a1000)
libz.so.1 => /usr/lib/libz.so.1 (0x7fa705d000)

As it turns out the interface (2) also uses UCP4 but directly via TCP, so no encryption and authentication.

The UCP4 protocol is fairly simple. There are request, responses and events. The client sends requests, the server replies with responses and events. All messages contain two parts with similar structure:

  • 8 bytes header: idx | 1 | 0 | 0 | length (4 bytes)
  • JSON string with size length

The first part has idx 1, the second part idx 2. The first part always has the same JSON elements:

{
“timestamp”:time in ms,
“type”:“request”, // or “response” or “event”
“action”:“resvsGet”,
“id”:“uuid
}

The “id” is the same in the request and response.
The second part contains some parameters for the action or response.

We can get a list of available actions using strings bleconnd:

ifStatusGet
ifAddrGet
freeResvsGet
resvsGet
resvCreate
resvDestroy
resvRecreate
scanStart
scanStop
connsGet
connOpen
connClose
connResvSet
connPhySet
connPhyGet
connParamsSet
connParamsGet
connLatencySet
connLatencyGet
connSignalGet
connDirectionGet
connAddressGet
connStatusGet
connMtuGet
gattcDbUpdate
gattcPrimaryServicesGet
gattcServiceGet
gattcCharGet
gattcDescGet
gattcCharValueRead
gattcDescValueRead
gattcCharValueWrite
gattcDescValueWrite
gattcCharValueNotifyConfirm
advsGet
advStart
advStop
advResvSet
advDataSet
advDataGet
advStatusGet
advAddressGet
advPhyGet
advChannelMapGet
advTxPowerGet
advIntervalGet
advConnectionGet
gattsPrimaryServicesGet
gattsServiceGet
gattsCharGet
gattsDescGet
gattsServiceAdd
gattsServiceRemove
gattsServiceEnable
gattsServiceDisable
gattsServiceReferenceAdd
gattsServiceReferenceRemove
gattsCharAdd
gattsCharRemove
gattsDescAdd
gattsDescRemove
gattsCharValueSetNotify
gattsCharValueReadResponse
gattsCharValueWriteResponse
gattsDescValueReadResponse
gattsDescValueWriteResponse

This looks quite promising, but up to now I was not able to get actual scan results via that interface.

You can try this iPython notebook to make your own experiments: UCP4 test · GitHub

There is another interface (3) and this shows that the required functionality is actually there. If you execute ubnt-btmw-rpc directly on the access point you get a constant update of bluetooth scan messages with MACs and other advertisment information from devices in range.

3 Likes