COVID-19 bluetooth tracker

I live in a shopping street and want to count the number of counts that it detects if someone is using the COVID-19 API on their Android or iOS device. I’m using an ESP32 with ESPHome to detect the precense. Al works well and it gives me a sensor in HA sensor.latest_coronamelder_detected

esp32_ble_tracker:

exposure_notifications:
  on_exposure_notification:
    then:
      - lambda: |
          ESP_LOGD("main", "Got notification:");
          ESP_LOGD("main", "  RPI: %s", hexencode(x.rolling_proximity_identifier).c_str());
          ESP_LOGD("main", "  RSSI: %d", x.rssi);
      - text_sensor.template.publish:
          id: latest_coronamelder_detected
          state: !lambda 'return hexencode(x.rolling_proximity_identifier).c_str();'

It gives me the following state (example): F2.F7.62.65.40.01.32.60.89.97.93.FA.24.8A.99.1A (16)

I now have a counter and automation to increment the counter by one, if the state of the sensor changes.
The problem with this is that if there if the sensor changes between 2 id’s it keeps counting.

My question, how do I count only if the state hasn’t come up yet, as there is no way you can get a duplicate id.

1 Like

Interessting idea (you should have added esphome somewhere in the tilte or posted in the esphome category)

The problem is, to count the ids distinct you have to store them (or a hash). You could use a std::map or std::set for that in lambda. Everytime you aa new exposure you put it into the set and return the size of the set. If the id is already there, it will not increase the the counter (and generate no new state).

But: The ESP32 modules have limited RAM (I don’t know how much) it will quickly fill up. As the IDs are rolling (the same device will only use the same id for a few minutes) you can propably clean the set/map of old entries every hour and it would work.

Another option: Push the IDs to Home Assistant and count the destinct ones there.

Well, I already have them in Home Assistant:

afbeelding
afbeelding

I just need a way to count them without duplicates

OK, I wrote a working version.

First you have to create a file “include_set.h”. Containing only one line:

#include <set>

To the esphome section add that:

esphome:
  name: corona
  platform: ESP32
  board: featheresp32
  includes:
    - include_set.h
  on_boot:
    priority: 801
    then:
      - lambda: |
          id(ids_seen) = new std::set<std::string>();

Add or change your a globals section:

globals:
   - id: ids_seen
     type: void*
     restore_value: no

And your exposure notifification section should look like:

exposure_notifications:
  on_exposure_notification:
    then:
      - if:
          condition:
            lambda: |
                auto ids_seen_set = (std::set<std::string>*)id(ids_seen);
                auto id_as_str = hexencode(x.rolling_proximity_identifier);
                if( !ids_seen_set->count( id_as_str ) )
                {
                  ids_seen_set->insert(id_as_str);
                  ESP_LOGD("main", "Got new notification:");
                  ESP_LOGD("main", "  RPI: %s", id_as_str.c_str() );
                  ESP_LOGD("main", "  RSSI: %d", x.rssi);
                  return true;
                }
                else
                {
                  ESP_LOGD("main", "Got existing notification:");
                  ESP_LOGD("main", "  RPI: %s", id_as_str.c_str() );
                  return false;
                }
          then:
              - text_sensor.template.publish:
                  id: latest_coronamelder_detected
                  state: !lambda 'return hexencode(x.rolling_proximity_identifier).c_str();'

It will push every new id into a set and only publish the state when a new id is seen.

Be car full: Every new id will take some RAM. Depending on your board and how many ids you see, your device can get out of memory. You could add an “interval” or “time” section with a daily interval to clear the list once a day/night. They are rolling (very 15 minutes) anyway. But: I think few 100 ids or more should not be problem.

I use a different version:


sensor:
 - platform: template
   name: "corona_warn_user_last_60s"
   update_interval: 60s
   unit_of_measurement: User
   accuracy_decimals: 0
   lambda: !lambda |-
       auto idset = (std::set<std::string>*)id(ids_seen);
       auto count = idset->size();
       idset->clear();
       return count;

esp32_ble_tracker:

exposure_notifications:
  on_exposure_notification:
    then:
      lambda: |
          auto ids_seen_set = (std::set<std::string>*)id(ids_seen);
          auto id_as_str = hexencode(x.rolling_proximity_identifier);
          if( !ids_seen_set->count( id_as_str ) )
          {
            ids_seen_set->insert(id_as_str);
            ESP_LOGD("main", "Got new notification:");
            ESP_LOGD("main", "  RPI: %s", id_as_str.c_str() );
            ESP_LOGD("main", "  RSSI: %d", x.rssi);
          }

That creates a sensor returning the number of users in range of the device using the Corona app.

Sadly the range of my esp32 is really low. Maybe one or two meters without walls.

To avoid storing that in the RAM, perhaps it would be possible to use store each connection made in a DB? And then do a select distinct on that table?
It would also give you the possibility of showing how many in a certain period of time.

What I am wondering is if the RIVM will allow you to push those IDs out to them and give the answer if there has been an infected person close by?
I guess not - that would be possible if you have an old phone with bluetooth turned on and the app lying at home all the time, though.

You can do that (you also have to store the extra data not just the id). But you have to implement the logic to match that data against the list of infected ids (at least for the implementation in Germany you can download that list without any authentication needed). But: The algorithm to match that data would be quite complicated because of all the encryption.

See: https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf

Hey @markisoke I also live on a busy street above a restaurant doing a ton of curbside pickup. I’ve implemented the @danielw version of the code and ran it for a bit and saw this:

I’m a bit unclear on what this information is showing me… Because the ID’s transmitted from the phones are rotated every 10 minutes, is this view showing all people detected in my bluetooth vicinity (~30 feet from my esp32) who have:
a) tested positive in the past 14 days by broadcasting a Diagnosis Key?
b) just opted in to transmit the program and is broadcasting an anonymous ID?

Here you get a very fast answer: :wink:

If you use my version of the code it will always show the amount of different ids it “saw” in the last 60 seconds. As every phone re transmits its id very often that should be about the number of people in reach of your ESP32 that are currently there transmitting the corona beacons. So if it currently shows a 6 like in your screenshot that means: “there are currently 6 phones in range of your device that are transmitting the corona app id”

The ids are rotated every x minutes (maybe 10 minutes) but that doesn’t really mater that much for the “last 60 seconds thing” it will lead to an about 9% too high number on average.

It does not know if those users were infected.

The general concept is: (simplified here, in reality there is quite a lot of hashing and encryption involved to reduce the amount of data and make it harder to game the system)

  • Your phone sends out a “random” id a few times a second. (the ids changes every 10 minutes). -
  • Your phone keeps a log of all the ids it has used in the last 14 days (together with the signal quality to estimate range)
  • Your phone also keeps keeps a log of all the ids from other phones it received in the last 14 days.

If you were to be tested positive, you will get a code or something to allow your phone to send the list of all ids it has transmitted in the last 14 days to a central server.

Every other phone will periodically fetch the list of infected ids from the central server and compare the list to the ids it has received from other devices nearby. Depending on the amount of matches and their signal quality the app will calculate and show you your risk.

In theory you could store the full ids your esp32 saw and fetch the list of infected ids and compare them.

1 Like