Help with ble_tracker - Switchbot Contact

I recently acquired a Switchbot Meter Plus and successfully got it working on an ESP32 with a ble_tracker using this lambda.

With that confident boost I figured that I could combine that lambda with the relevant packet parsing from the SwitchBot-MQTT-BLE-ESP32 project in order to get a Switchbot Contact working.

I quickly ran into memory issues using both the Meter and Contact on a single ESP32 and learned that including a safe_mode switch is very very very important when your device no longer OTA’s.

Breaking out a Huzzah32 from the ESP32 drawer, I have the button, motion and contact working. Except it isn’t exactly ‘realtime’ state reporting.

Playing with the different scan_parameters: (interval/window) has improved it a little, but there is still lag. Enough that the motion/contact sensor would be of diminished value as automation triggers in my eyes.

Since I am rather new to ble_tracking, I am hoping for a little insight/experience that others might have to offer in getting this working without a second or more lag in device action to reported state change and often outright missing changes.

Pinging @myhomeiot

esphome:
  name: huzzah32-ble  

esp32:
  framework:
    type: arduino
  board: featheresp32

# Enable logging
logger:
  logs:
    esp32_ble_tracker: INFO

# Enable Home Assistant API
api:
  reboot_timeout: 4h

ota:
  password: !secret more_secrets

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot in case wifi connection fails
  ap:
    ssid: "huzzah32-ble Fallback Hotspot"
    password: !secret more_secrets

mdns:
  disabled: true

substitutions:
  device_name: huzzah32-ble
  
web_server:
  port: 80
  version: 2
  include_internal: true
  ota: false
  auth:
    username: admin
    password: !secret web_server_password

button:
  - platform: restart
    name: Restart $device_name 

  - platform: template
    name: "Switchbot Button Pressed"
    id: switchbot_contact_button_pressed
    on_press:
      - logger.log: Contact Button Pressed
      - homeassistant.event:
          event: esphome.contact_button_pressed
          data:
            message: Contact button was pressed
    
switch:
  - platform: safe_mode
    internal: true
    name: use_safe_mode    
    
sensor:    
  - platform: template
    name: "Contact Battery"
    id: switchbot_contact_battery
    unit_of_measurement: '%'
    device_class: battery
    accuracy_decimals: 0
    icon: "mdi:battery"
    update_interval: 180s

  - platform: template
    name: "Contact Button"
    id: switchbot_contact_button
    unit_of_measurement: '#'
    internal: true
    
binary_sensor:
  - platform: template
    name: "Contact Motion"
    id: switchbot_contact_motion
    device_class: motion
    
  - platform: template
    name: "Contact contact"
    id: switchbot_contact_contact
    device_class: door 
    
esp32_ble_tracker:
  scan_parameters:
    interval: 320ms
    window: 320ms    
  on_ble_advertise:
    - mac_address: !secret MAC
      then:
        - lambda: |-
            for (auto data : x.get_service_datas()) {
                if(data.data.size() == 9) {
                  bool aMotion = false;
                  bool contact = false;
                  bool contactA = false;
                  bool contactB = false;
                  int8_t battLevel = 0;
                  
                  // door
                  contactA = data.data[3] & 0b00000100;
                  contactB = data.data[3] & 0b00000010;
                  if (!contactA && !contactB) {
                    contact = false;
                  }
                  else if (!contactA && contactB) {
                    contact = true;
                  }
                  id(switchbot_contact_contact).publish_state(contact);
                  
                  // motion & battery
                  aMotion  = data.data[1] & 0b01000000;
                  battLevel = data.data[2] & 0b01111111;
                  id(switchbot_contact_motion).publish_state(aMotion);
                  id(switchbot_contact_battery).publish_state(battLevel);
                  
                  // button
                  int buttonCountA = (data.data[8] & 0b00001000) ? 8 : 0;
                  int buttonCountB = (data.data[8] & 0b00000100) ? 4 : 0;
                  int buttonCountC = (data.data[8] & 0b00000010) ? 2 : 0;
                  int buttonCountD = (data.data[8] & 0b00000001) ? 1 : 0;
                  int buttonCount = buttonCountA + buttonCountB + buttonCountC + buttonCountD;
                  if ( buttonCount != id(switchbot_contact_button).state) {
                    id(switchbot_contact_button_pressed).press();
                  }
                  id(switchbot_contact_button).publish_state(buttonCount);
                }
            }
    ```
1 Like

After further testing the following seems to help in short term bench-testing.

  scan_parameters:
    interval: 320ms
    window: 320ms

Other observations include;

  • the button counts from 1 to 15 then resets back to 1. Something to consider for the purposes of automations.
  • performing an OTA is extremely slow when using ble_tracker and the new Safe Mode switch is not only a life-saver when things go wrong but helps speed up OTA as well.

[edit] - With a little time to think about how to use the button to trigger automations, I have come up with the following based on the docs suggestion. Pressing the contact button will send an event to HA which can be used as an automation trigger.

  trigger:
  - platform: event
    event_type: esphome.contact_button_pressed

I have updated the OG yaml to reflect.

Hey,

Just wanted to say thanks, I was trying to get info out of the contact sensor myself and your code just works. Much appreciate you writing it up !

That’s good to hear! There are a lot of BLE options out there, glad it wasn’t a fluke :stuck_out_tongue:

Just one thing I changed now :

                  if (!contactA && !contactB) {
                    contact = false;
                  }
                  else {
                    contact = true;
                  }

Looking at the doc there’s only one closed state, and two open states (open or timeout open). In your code you consider the timeout open state as a closed state, so I just removed the second condition to consider anything that’s not closed as open. With the above if the door is left open longer than the timeout, it keeps showing open in HA

Thanks for catching that :slight_smile:

The code is incomplete. The contact sensor has three states: close / open / left open.
To get the open state even on “left open” I’ve inserted this third case:

                  // door
                  contactA = data.data[3] & 0b00000100; 
                  contactB = data.data[3] & 0b00000010;
                  if (!contactA && !contactB) {
                    contact = false;
                  }
                  else if (!contactA && contactB) { 
                    contact = true;
                  }
                  else if (contactA && !contactB) {
                    contact = true;
                  }
                  id (${esp32_name}_fenster_bad_contact).publish_state(contact);

Choose whichever works for your use-case Help with ble_tracker - Switchbot Contact - #5 by Ulrar