From time to time people ask me how to do the reverse engineering of a device to integrate it into Home Assistant. Sure, reverse engineering sounds fancy and will get you a better position in the search engine’s results but let’s be honest it’s not much magic or rocket science and most of the time it’s just putting the pieces together you found. I prefer to talk about it as “using devices beyond the original purpose”.
audius provided me with a Troika SELMATE (KR15-03/WH). It’s a Bluetooth Key/Smartphone Finder. The haptics are excellent and the quality is higher than the standard for such devices.

I usually don’t need to find my keys because my Yubikey is attached and without that I’m not able to unlock my computer. Same for my Smartphone which contains the 2FA app. Both things are most of the time pretty close to me.
The Troika SELMATE can be used in two ways if the device is paired with your smartphone: One press would create a selfie and two presses are activating the alarm on the smartphone. A click on the “Alert” button in the Troika Find app let the device blink and beep. Thus, there are multiple ways to use the device beside the Troika app: simple monitoring of its state, perhaps switching a light on, use it as an alarm device, as physical part of a self-made alarm clock and so on and on 
First let’s see what we can find out. There is no special hardware required. I’m using a Lenovo T460 with a built-in Bluetooth adapter and a Fedora 27 installation. The first tool we are going to use is
hcitool
which is part of
bluez
. Perform the following command to install it.
$ sudo dnf -y install bluez
Switch the Troika SELMATE on and run a scan with
hcitool
.
$ sudo hcitool lescan
LE Scan ...
08:7C:BE:78:C9:A0 SELFMATE
08:7C:BE:78:C9:A0 (unknown)
...
If you have other Bluetooth device like your smartphone or our mouse then they will show up as well. Make sure that the SELFMATE is not paired with your smartphone anymore. Let’s see what else is available.
$ sudo hcitool leinfo 08:7C:BE:78:C9:A0
Requesting information ...
Handle: 3586 (0x0e02)
LMP Version: 4.0 (0x6) LMP Subversion: 0x400
Manufacturer: Quintic Corp. (142)
Features: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Not much so far. Ok, next tool.
gatttool
is also part of
bluez
. Establishing a connection can be done in an interactive way.
$ sudo gatttool -b 08:7C:BE:78:C9:A0 -I
[08:7C:BE:78:C9:A0][LE]> connect
Attempting to connect to 08:7C:BE:78:C9:A0
Connection successful
The first ting we are going to do is to the characteristic handles and service UUIDs implemented by the device.
[08:7C:BE:78:C9:A0][LE]> primary
attr handle: 0x0010, end grp handle: 0x0012 uuid: 00001803-0000-1000-8000-00805f9b34fb
attr handle: 0x0013, end grp handle: 0x0015 uuid: 00001802-0000-1000-8000-00805f9b34fb
attr handle: 0x0016, end grp handle: 0x0018 uuid: 00001804-0000-1000-8000-00805f9b34fb
attr handle: 0x0019, end grp handle: 0x001c uuid: 0000ffe0-0000-1000-8000-00805f9b34fb
attr handle: 0x001d, end grp handle: 0x001f uuid: 000018f0-0000-1000-8000-00805f9b34fb
attr handle: 0x0020, end grp handle: 0x0023 uuid: 0000180f-0000-1000-8000-00805f9b34fb
Looking them up with the help of the Bluetooth services list will give us an idea about what the device is supporting.
Those two handles which are not documented.
- 0000ffe0-0000-1000-8000-00805f9b34fb
- 000018f0-0000-1000-8000-00805f9b34fb
The most import one seems to be “Immediate Alert”. This is the trigger to let the device beep and blink.
0x0015 is the group handle for the UUID 0x1802. Limiting the output to 0x0015 will give us the characteristic for the alarm.
[08:7C:BE:78:C9:A0][LE]> char-desc 0x0015 0x0015
handle: 0x0015, uuid: 00002a06-0000-1000-8000-00805f9b34fb
0x2a06 is the UUID for the Alert Level. There are three levels available but only two are useful with a device like a keyfinder.
- 0x00 (No alert): Nothing
- 0x01 (Mild alert): Beeping
- 0x02 (High alert): Beeping and Blinking
Now, we know tht handle and the possible values. This means that we control the device in the way we want.
[08:7C:BE:78:C9:A0][LE]> char-write-cmd 0x0015 01
[08:7C:BE:78:C9:A0][LE]> char-write-cmd 0x0015 02
Like for the app…with a press you can confirm the alarm.
You can also get the battery level (Battery Service 0x180f, Battery Level 0x2a19)
[08:7C:BE:78:C9:A0][LE]> char-read-uuid 0desc 0x0022 0x0022
handle: 0x0022, uuid: 00002a19-0000-1000-8000-00805f9b34fb
[08:7C:BE:78:C9:A0][LE]> char-read-uuid 00002a19-0000-1000-8000-00805f9b34fb
handle: 0x0022 value: 63
Keep in mind that the value is HEX.
$ python3 -c "print(int('63', 16))"
99Nice, the battery level is still at 99 %. We don’t care about Link Loss and Tx Power for now.
Next, get the button press. There are two UUID left. At the moment we don’t know much about the remain services or their settings. Without a third-party tool it’s try-and-error with going through possible values.
gatttool
needs be in listen mode to receive the signal.
The first two approaches don’t work.
$ gatttool -b 08:7C:BE:78:C9:A0 --char-write-req --handle=0x001c --value=0000 --listen
$ gatttool -b 08:7C:BE:78:C9:A0 --char-write-req --handle=0x001c --value=0010 --listen
Here we go:
$ gatttool -b 08:7C:BE:78:C9:A0 --char-write-req --handle=0x001c --value=0100 --listen
Characteristic value was written successfully
Notification handle = 0x001b value: 01
As an example we are going to integrate the Battery level into Home Assistant. Battery level sound like a sensor. Create a file called
troika.py
in your configuration directory for your custom components. It will become a sensor thus the path will something like that
.homeassistant/custom_components/sensor/troika.py
. To make the Home Assistant implementation simple, the subprocess module is executing the
gatttool
. This is not for production usage but for quick tests it sufficient. Also, if multiple platform using the same base then it should become a component.
Copy this code to your
troika.py
file.
"""Support for Troika keyfinder's Battery status."""
import logging
import subprocess
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (CONF_MAC, CONF_NAME)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
BLE_BATTERY_HANDLE = '0x0022'
DEFAULT_NAME = 'Troika Battery'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_MAC): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Troika Battery sensor."""
name = config.get(CONF_NAME)
mac = config.get(CONF_MAC)
add_devices([TroikaBattery(name, mac)], True)
class TroikaBattery(Entity):
"""Representation of a Troika Battery sensor."""
def __init__(self, name, mac):
"""Initialize a Troika Battery sensor."""
self._state = None
self._mac = mac
self._name = name
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def state(self):
"""Return the state of the device."""
return self._state
@property
def unit_of_measurement(self):
"""Return the unit the value is expressed in."""
return "%"
def update(self):
"""Get the latest data and updates the states."""
self._state = self.get_battery()
def get_battery(self):
"""Get the battery level from the SELFMATE."""
command = ['gatttool', '-b', self._mac, '--char-read', '-a',
BLE_BATTERY_HANDLE]
battery = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
try:
out, _ = battery.communicate()
return int(out.decode().split()[2], 16)
except subprocess.CalledProcessError:
return NoneDon’t forget to add the new sensor to your
configuration.yaml
file:
sensor:
- platform: troika
mac: "08:7C:BE:78:C9:A0"After a restart of Home Assistant the new sensor should show up. It seems that the battery is draining quickly.

Let the SELFMATE start beeping is like switching something on or off. For a home automation solution this can be done with a switch. Create a file again in the
custom_components
directory but this time in the switch folder, e.g.,
.homeassistant/custom_components/switch/troika.py
The code for the switch is uning pretty much the same elements as the sensor.
"""Support for Troika keyfinder's Beeping mode."""
import logging
import subprocess
import voluptuous as vol
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_MAC, CONF_NAME)
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
BLE_ALERT_HANDLE = '0x0015'
DEFAULT_NAME = 'Troika Beeping'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_MAC): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Troika SELFMATE."""
name = config.get(CONF_NAME)
mac = config.get(CONF_MAC)
add_devices([TroikaBeep(name, mac)], True)
class TroikaBeep(SwitchDevice):
"""Representation of a Troika Battery sensor."""
def __init__(self, name, mac):
"""Initialize a Troika Battery sensor."""
self._state = None
self._mac = mac
self._name = name
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def is_on(self):
"""Return true if device is on."""
return self._state
def update(self):
"""Get the latest data and updates the states."""
return self._state
def turn_on(self, **kwargs):
"""Turn the device on."""
self._state = True
alert_off = ['gatttool', '-b', self._mac, '--char-write-req', '-a',
BLE_ALERT_HANDLE, '-n', '01']
alert = subprocess.Popen(
alert_off, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
try:
alert.communicate()
return alert.returncode == 0
except subprocess.CalledProcessError:
return None
def turn_off(self, **kwargs):
"""Turn the device off."""
self._state = False
alert_off = ['gatttool', '-b', self._mac, '--char-write-req', '-a',
BLE_ALERT_HANDLE, '-n', '00']
alert = subprocess.Popen(
alert_off, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
try:
alert.communicate()
return alert.returncode == 0
except subprocess.CalledProcessError:
return NoneTo use the switch add it to your
configuration.yaml
file:
switch:
- platform: troika
mac: "08:7C:BE:78:C9:A0"Implementing the button press is a bit trickier than just monitoring a value. If you want to react on a button press then Home Assistant needs to listen all the time like
gatttool
with
--listen
. If you are interested then I suggest that you take a look at the existing platforms for a proper integration of Bluetooth devices.