I've written before about using an ESP32 to do Better Presence Detection with Home Assistant and ESPresense. ESPresense works by using ESP32s positioned in different rooms to track the relative movement of a person by monitoring the signal strength of a BLE device that they carry (such as a smartphone).

I've been using ESPresense for seven months now, and it has been a very reliable way to know which room someone is likely to be in based on where their smartphone is in the house.

Someone who read my original blog post on ESPresense got in touch recently to ask if it's possible to easily enumerate which devices are in a room. Naturally, I was curious to know if this was possible and to see what I could find out.

Recap: ESPresense Basics

The basic premise of ESPresense is that you will need to place a device running ESPresense in each room that you want to perform presence detection. This way, a device you are tracking (e.g. a smartphone) can be associated with being in the room with the ESPresense device with the strongest signal, inferring the stronger the signal, the closer you are to the ESPresense device.

ESPresense transmits two different sets of topics:

  • espresense/devices/*
    Any device picked up by ESPresense will be tracked under a Device ID and provided its own topic where information is pushed about the RSSI signal strength of ESPresense devices that can see the BLE beacon being tracked
  • espresense/rooms/*
    Each ESPresense device will publish a room topic that will publish basic information about the ESPresense device

In Home Assistant, we're able to configure in our sensor.yaml the devices that we want to track within Home Assistant, for example:

- platform: mqtt_room
  name: "James iPad BLE"
  device_id: "roomAssistant:xxxx-xxxx-xxxx"
  state_topic: "espresense/devices/roomAssistant:xxxx-xxxx-xxxx"
  timeout: 5
  away_timeout: 120
- platform: mqtt_room
  name: "James iPhone BLE"
  device_id: "roomAssistant:xxxx-xxxx-xxxx"
  state_topic: "espresense/devices/roomAssistant:xxxx-xxxx-xxxx"
  timeout: 5
  away_timeout: 120

Home Assistant will then track my iPhone and iPad. Using the information published about each device, Home Assistant will determine which room I am in and save that against the state of "James iPad BLE" and "James iPhone BLE", respectively.

The result is that there isn't an easy way that I've come across to list or monitor all devices in a given room.

In my original ESPresense blog post, I talked about writing automations to turn off lights if no one was home, for example:

alias: Office Off - Unoccupied
description: "Turn the lights off if James isn't in the office for more than 10 minutes"
mode: single
trigger:
  - platform: state
    entity_id:
      - sensor.james_iphone_ble
    from: office
    for: 00:10:00
action:
  - service: light.turn_off
    data: {}
    target:
      entity_id:
        - light.office_lights
  - service: switch.turn_off
    data: {}
    target:
      entity_id: switch.office_lights

The key bit in the above snippet is the trigger section. However, if you want to track quite a few devices, this could be quite painful to manage as you'd have to update every automation with each device you want to track.

What if you could easily track the number of devices in each room instead of having to check that each device being tracked isn't in the room?

Counting Room Occupancy

Counting the number of devices in a room could be a simpler way to determine if a room is occupied or not, rather than having to check the state of each device in a trigger condition of every automation that you want to be based on room occupancy.

The best way that I've found to do this is via a template sensor which I've added to my sensor.yaml:

- platform: template
  sensors:
    office_occupancy:
      friendly_name: Office Occupancy
      value_template: >
        {% set ns = namespace(occupancy=0) %}
        {%- for sensor in states.sensor %}
          {%- if 'distance' in sensor.attributes and sensor.state == 'office' %}
            {%- set ns.occupancy = ns.occupancy + 1 %}
          {%- endif %}
        {%- endfor %}
        {{ ns.occupancy }}

You would need to create an occupancy sensor for each room where you wanted to monitor the occupancy count. I've created an occupancy sensor for my office in the above example.

This works by enumerating all sensors with a distance attribute and incrementing a count if they are in the given room. In an ideal world, the template would check that the platform for the sensor is set to mqtt_room, however it doesn't look like it is possible to interrogate the platform value of the sensor. Whilst this possibly isn't the most watertight way of doing this, my testing has shown that the only devices I have that have a distance attribute are mqtt_room sensors that I have setup. If I wanted I could aim to make the more robust, and in the absense of being able to check that the platform is set to mqtt_room I could check that the sensor name/ID includes BLE as I've used a consistent naming convention when setting up the various mqtt_room sensors.

The state value of my occupancy sensor is simply the number of BLE devices being tracked that have a state of "office". At the moment, I'm sat in my home office writing this blog post, and both my iPhone and iPad are on the desk, as a result the occupancy count for the room shows as 2.

Office Occupancy sensor in Home Assistant showing the number of devices in the room
Office Occupancy sensor in Home Assistant showing the number of devices in the room

This means that I have a more generic way of determining if a room is occupied moving forwards. Rather than needing to list individual devices in trigger conditions of automations I can simply check an occupancy sensor to see if the count is 0 or >0.

This feels to me like a simple way of determining an occupancy coutn for a room, but if anyone knows of a better way to do this, please let me know!