Step-by-step presence detection with bluetooth tag (e.g., GTags) and ESPresense on ESP32

Since this MQTT, subscribing and parsing topic, while it refers to ESPresense (, GitHub: ESPresense · GitHub), does not give enough detail for beginners to get this up and running easily and the ESPresense website focuses on HomeAssistant config, I’ll try to leave a few notes here that may help other beginners to get up and running if the aim is to get presence detection with bluetooth tags (like the GTags, other bluetooth devices, including mobiles, for which ESPresense has some fingerprinting features apparently, to address random MAC changes, should work similarly).

NOTE: I’m an OpenHab/MQTT newbie, so there may be way more elegant ways to do this (implementing the below requires quite a bit of clicking around). Any advice most welcome!

  • Step 1: get ESP32 or similar with ESPresense firmware up and running by following the instructions given here: Install – ESPresense – ESP32 based indoor positioning system
  • Step 2: install Mosquitto MQTT broker on OpenHab server (Raspi in my case), by selecting Optional Components/Mosquitto in openhabian-config; set adequate password and make note of this; install MQTT binding and JSONPATH transformation add on in OpenHab unless already present
  • Step 3: Set your ESPresense ESP32 to target your mosquitto by setting IP to OpenHab server IP, user ID to openhabian and password to the Mosquitto password you set via the ESPresense web UI
  • Step 4: check MQTT message stream on your Moquitto by running mosquitto_sub -v -u openhabian -P YOURMOSQUITTOPASSWORD -h localhost -p 1883 -t '#' on your OpenHab server; you should see a stream of espresence/# related MQTT messages on your console
  • Step 5: create MQTT broker thing in OpenHab
  • Step 6: Use autodetection for HomeAssistant things (should show up in your thing inbox) to get a first representation of ESPresense ESP32, which gives status, firmware, etc.
  • Step 7: create one generic MQTT thing using your MQTT broker configured in step 5 per ESP32 ESPresense device; create equipment from this thing in the appropriate room in your semantic model; you can group the other stuff there in the following steps
  • Step 7a: create status channel on this thing as ON/OFF switch type channel, with MQTT State topic “espresense/rooms/[ESP 32 ID]/status”; create status item from this channel; this seems to respond more fluidly to status changes of the ESP32 than the autodiscovered status from Step 6
  • Step 8: create one string typed MQTT “JSON complete” channel per ESP32 per bluetooth device you would like to detect, with state topic “espresense/devices/[bluetooth tag MAC]/[ESP32 ID]” as per the MQTT stream from step 4 (you can to identify your bluetooth tag MACs from the MQTT message stream by moving them around a little and looking at distance changes or whatever)
  • Step 9: from the “JSON complete” channel context, by using “create points”, create one number item per data item per tag per ESP32 that you want to use in OpenHab (I use distance, rssi, speed for each GTag on each ESP32)
  • Step 10: configure the “JSONPATH” profile for each item/channel link, inserting JSONPATH expression as appropriate, i.e., “$.distance”, “$.rssi”, “$.speed”, without quotes, of course).
  • Step 11: add metadata to item, i.e. for units (state description “%.1f m” to add meters unit to distance, “%.1f dB” for RSSI, etc.) and Expiration Timer to make sure you notice when the tag is gone (I currently use 10 s expiration).
  • Step 12: make some rules to fuse you various ESP32 distance values etc. into something you can build further logic on.

Optional/additional GTag detection using Raspi builtin Bluetooth: In addition to using the ESP32s for detecting the GTags, one can use the OpenHab Bluetooth binding and the BlueZ stack on the Raspi (if that’s what your OpenHab is running on) to cover the vicinity of the Raspi using the Raspi builtin Bluetooth hardware. After successful installation/configuration of the Bluetooth binding, just make sure to add the autodiscovered things of type “beacon”, not “generic” (sometimes takes a few tries), which then give you an RSSI channel, and add an expiration timer to the RSSI items build from the “beacon” things. Works like a charm for GTags, no need for non-standard bluetooth binding in the current version of OpenHab to get this to work, from my experience. Useful in our case so we don’t need an additional ESP32 in the basement.


That’s great! Thanks!
Do you have any experience on how this compares to room-asssistant?

The whole room presence detection is still on my to-do list.

Sorry, I have no first hands experience with Home Assistant (if that’s what you’re referring to). Decided to go with OpenHab since it’s supposedly more robust for production use. I also like the tech stack better.

Talking about room-asssistant which I saw the ESP32 version is based on.
I run a room-assistant instance for testing since a few months and it seems that the overall functionality is similar.

Ah, I see. No hands on experience there, either, sry.

I have built something similar to room assistant that uses the OH model. It’s done in jython

I moved this to the tutorials and solutions section where it can more easily be found.


I’m also starting to use espresense but I think I found an easier way :slight_smile:

I added a channel to my broker to get all MQTT updates in the devices topic for a special device:

“14_Pro_Max” is my in the espresense configured and monitored iPhone. With the “#” I receive alle keys and values for handling later in a rule.

After that I created a dummy String item for visualising the room name.

I created following rule to check for the room name and set it the previous created dummy String item:

var klausLastRoom = ""
var klausLastDistance = 10.0

rule "espresense_room_klaus"
        Channel "mqtt:broker:Matt_broker:c_14" triggered
        val json = receivedEvent.split('#').get(1)
        val distance =  transform("JSONPATH", "$.distance", json)
        val distanceDouble =  Double.parseDouble(distance)
        val room = receivedEvent.split('#').get(0).split('/').get(3)
        //checking if the room is the same current
        if(klausLastRoom != room){
        	logInfo("espresense_log", "Checking new room: " + room)
        	//checking if new room is closer than current one
        	if(distanceDouble < klausLastDistance){
        		klausLastRoom = room
        		klausLastDistance = distanceDouble
        		// translating the technical espresense names to human readable ones (hard coded)
        		if(klausLastRoom == "esp_og_eltern"){
        		}else if(klausLastRoom == "esp_eg_living_room"){
        		}else if(klausLastRoom == "esp_ug_office"){
        		}else if(klausLastRoom == "esp_ug_cellar"){
        			iphone14Room.sendCommand("Unbekannter Raum")
        		logInfo("espresense_log", "New room for device detected: " + room)
        		logInfo("espresense_log", "Room ist too far away: " + room + ". Current distance is " + klausLastDistance + " vs. " +  distanceDouble)
        // update distance for current room	
       	 	logInfo("espresense_log", "Current room info for: " + room)
       	 	klausLastDistance = distanceDouble
       	 	logInfo("espresense_log", "Updated distance for current room: " + klausLastDistance)

For other devices a new channel for the broker, a new dummy String item and a new rule is needed (easy copy paste modify)

Hope it helps and saves you some time :slight_smile: