ESPHome Bluetooth Tracker for Relsib WT50 Thermometer

Complete ESPHome configuration for monitoring Relsib WT50 Bluetooth thermometer with reliable connection management and manual stop functionality.

Features

  • Real-time temperature and battery data transmission to Home Assistant
  • Automatic connection when thermometer is available
  • Manual stop button with 3,5-minute cooldown period
  • Three clear status states: Waiting, Connected, Blocked
  • Reliable connection management without interference with thermometer auto-shutdown

Important Notes

Thermometer Behavior

  • Automatically powers on when heated
  • Transmits data every 1 second via Bluetooth
  • Auto-shutdown : Powers off after 180 seconds (3 minutes) if no device is connected
  • Manual shutdown available via mobile app “Disconnect” button

ESP32 Tracker Logic

  • Always attempts to connect when thermometer is advertising
  • Real-time data publishing to Home Assistant
  • Manual stop button : Forces disconnection and blocks reconnection for 3,5 minutes
  • Blocking period : Ensures thermometer has time to auto-shutdown completely
  • Status indicators :
    • Connected : Connected and receiving data
    • Waiting : Ready to connect, thermometer likely off
    • Blocked : Manual stop active, waiting for thermometer shutdown

Configuration

substitutions:
  thermo_mac: "54:6C:0E:59:53:0B"

esphome:
  name: thermometer
  friendly_name: Thermometer

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf

logger:
  level: INFO

api:
  encryption:
    key: "your_encryption_key_here"

ota:
  - platform: esphome
    password: "your_ota_password"

wifi:
  ssid: "Your_WiFi_SSID"
  password: "Your_WiFi_Password"
  min_auth_mode: WPA2
  manual_ip:
    static_ip: 192.168.1.100
    gateway: 192.168.1.1
    subnet: 255.255.255.0
    dns1: 192.168.1.1

globals:
  - id: current_temperature
    type: float
    restore_value: yes
    initial_value: '0.0'
  - id: battery_level
    type: int
    restore_value: yes
    initial_value: '0'
  - id: block_until
    type: int64_t
    restore_value: no
    initial_value: '0'
  - id: last_connect_attempt
    type: int64_t
    restore_value: no
    initial_value: '0'

esp32_ble_tracker:
  scan_parameters:
    interval: 1280ms
    window: 320ms
    active: true

ble_client:
  - mac_address: "${thermo_mac}"
    id: thermometer_ble
    auto_connect: false

sensor:
  - platform: ble_client
    type: characteristic
    ble_client_id: thermometer_ble
    service_uuid: "00001809-0000-1000-8000-00805f9b34fb"
    characteristic_uuid: "00002a1e-0000-1000-8000-00805f9b34fb"
    name: "Temperature Intermediate"
    id: temp_raw
    internal: true
    notify: true
    lambda: |-
      if (x.size() == 5) {
        uint8_t flags = x[0];
        bool fahrenheit = (flags & 0x01) == 0x01;
        uint32_t mantissa = (uint32_t)x[1] | ((uint32_t)x[2] << 8) | ((uint32_t)x[3] << 16);
        int8_t exponent = x[4];
        float temperature = mantissa * pow(10, exponent);
        if (fahrenheit) {
          temperature = (temperature - 32) * 5.0 / 9.0;
        }
        id(current_temperature) = temperature;
        id(temperature_sensor).publish_state(temperature);
        ESP_LOGI("main", "Temperature: %.2f°C", temperature);
      }
      return {};

  - platform: ble_client
    type: characteristic
    ble_client_id: thermometer_ble
    service_uuid: "0000180f-0000-1000-8000-00805f9b34fb"
    characteristic_uuid: "00002a19-0000-1000-8000-00805f9b34fb"
    name: "Battery Raw"
    id: batt_raw
    internal: true
    notify: true
    lambda: |-
      if (x.size() == 1) {
        id(battery_level) = x[0];
        id(battery_sensor).publish_state(x[0]);
        ESP_LOGI("main", "Battery: %d%%", x[0]);
      }
      return {};

  - platform: template
    name: "Body Temperature"
    id: temperature_sensor
    unit_of_measurement: "°C"
    icon: "mdi:thermometer-probe"
    accuracy_decimals: 2
    device_class: temperature
    state_class: measurement
    filters:
      - delta: 0.05
      - throttle: 2s
    lambda: 'return id(current_temperature);'

  - platform: template
    name: "Thermometer Battery"
    id: battery_sensor
    unit_of_measurement: "%"
    icon: "mdi:battery"
    accuracy_decimals: 0
    device_class: battery
    lambda: 'return id(battery_level);'

text_sensor:
  - platform: template
    name: "Thermometer Status"
    id: status_text
    icon: "mdi:thermometer-bluetooth"
    lambda: |-
      uint64_t now = millis();
      if (now < id(block_until)) {
        return std::string("Blocked");
      } else if (id(thermometer_ble)->connected()) {
        return std::string("Connected");
      } else {
        return std::string("Waiting");
      }
    update_interval: 5s

  - platform: wifi_info
    ip_address:
      name: "IP Address"
    ssid:
      name: "WiFi SSID"
    mac_address:
      name: "MAC Address"

binary_sensor:
  - platform: status
    name: "Status"

button:
  - platform: template
    name: "Stop Thermometer"
    id: stop_button
    icon: "mdi:stop-circle"
    on_press:
      then:
        - lambda: |-
            ESP_LOGI("main", "Manual stop - blocking for 3.5 minutes");
            id(block_until) = millis() + 210000;
            if (id(thermometer_ble)->connected()) {
              id(thermometer_ble)->disconnect();
            }
            id(last_connect_attempt) = 0;
            ESP_LOGI("main", "Thermometer blocked for 3.5 minutes");

interval:
  - interval: 30s
    then:
      - lambda: |-
          uint64_t now = millis();
          if (id(block_until) > 0 && id(block_until) - now > 210000) {
            ESP_LOGI("main", "Resetting block_after reboot");
            id(block_until) = 0;
            return;
          }
          if (now < id(block_until)) {
            int64_t remaining = (id(block_until) - now) / 1000;
            ESP_LOGI("main", "Blocked: %llds", remaining);
            if (id(thermometer_ble)->connected()) {
              ESP_LOGI("main", "Disconnecting due to block");
              id(thermometer_ble)->disconnect();
            }
            return;
          }
          if (id(thermometer_ble)->connected()) {
            ESP_LOGI("main", "Thermometer connected");
          } else if (now - id(last_connect_attempt) > 60000) {
            id(last_connect_attempt) = now;
            ESP_LOGI("main", "Attempting to connect");
            id(thermometer_ble)->connect();
          }

How It Works

Normal Operation Cycle:

  1. Thermometer activation → 20-sec warmup → Starts BLE advertising
  2. ESP32 connection → Detects advertisement → Connects
  3. Data transmission → Sends temperature every second → To Home Assistant
  4. Disconnection:
  • Normal : Remove thermometer → Stops transmitting → ESP disconnects → Thermometer powers off in 180s
  • Manual : Press “Stop” button → ESP disconnects immediately → Thermometer powers off in 180s → ESP blocks reconnection for 210s (180s + 30s buffer)

Manual Stop Operation:

  1. User presses “Stop Measurement” in Home Assistant (useful for terminating measurement early)
  2. ESP32 immediately disconnects
  3. BLE client disabled for 210 seconds (3,5 minutes)
  4. Status shows “Blocked”
  5. Thermometer auto-shutdowns within 180 seconds
  6. After 3,5 minutes, ESP32 resumes normal operation

Thermometer Not Powering Off

  • Important : After pressing “Stop Measurement”, the 3,5-minute cooling period ensures the thermometer can auto-shutdown
  • The thermometer needs 180 seconds of no connections to power off
  • ESP32 blocks all connections for 210 seconds to guarantee shutdown

My current configuration

esphome:
  name: termometer
  friendly_name: termometer

esp32:
  board: esp32-c3-devkitm-1  
  framework:
    type: esp-idf

logger:
  level: INFO

api:
  encryption:
    key: "YOUR_ENCRYPTION_KEY"

ota:
  - platform: esphome
    password: "YOUR_OTA_PASSWORD"

wifi:
  ssid: "YOUR_WIFI_SSID"
  password: "YOUR_WIFI_PASSWORD"  
  manual_ip:
    static_ip: YOUR_STATIC_IP
    gateway: YOUR_GATEWAY
    subnet: YOUR_SUBNET
    dns1: YOUR_DNS
  output_power: 9.5dB  
  fast_connect: true
  passive_scan: false
  
globals:
  - id: termometer_temperature
    type: float
    restore_value: yes
    initial_value: '0.0'
  - id: termometer_battery_current
    type: int
    restore_value: yes
    initial_value: '0'
  - id: block_until
    type: int64_t
    restore_value: no
    initial_value: '0'
  - id: termometer_ble_connected
    type: bool
    restore_value: no
    initial_value: 'false'

esp32_ble_tracker:
  scan_parameters:
    interval: 1200ms
    window: 400ms
    active: false

ble_client:
  - mac_address: "YOUR_DEVICE_MAC_ADDRESS"
    id: relsib_wt50
    auto_connect: false
    on_connect:
      then:
        - lambda: |-           
            id(termometer_ble_connected) = true;
    on_disconnect:
      then:
        - lambda: |-          
            id(termometer_ble_connected) = false;                

interval:
  - interval: 3s
    then:
      - text_sensor.template.publish:
          id: termometer_status
          state: !lambda |-
            uint64_t now = millis();
            if (now < id(block_until)) {
              if (id(relsib_wt50)->connected()) {
                id(relsib_wt50)->disconnect();
              }
              return "Blocked";  
            } else if (id(termometer_ble_connected)) {
              return "Connected";
            } else {
              return "Waiting";
            }
      - lambda: |-
          static uint64_t last_connect_attempt = 0;
          uint64_t now = millis();          
          if (now < id(block_until)) {
            return;
          }          
          if (!id(relsib_wt50)->connected() && 
              id(termometer_detected).state &&
              (now - last_connect_attempt > 10000)) { 
            last_connect_attempt = now;
            id(relsib_wt50)->connect();
          }

sensor:
  - platform: ble_client
    type: characteristic
    ble_client_id: relsib_wt50
    service_uuid: "00001809-0000-1000-8000-00805f9b34fb"
    characteristic_uuid: "00002a1e-0000-1000-8000-00805f9b34fb"
    name: "Temperature Intermediate"
    id: temp_sensor
    internal: true
    notify: true
    lambda: |-
      if (x.size() == 5) {
        uint8_t flags = x[0];
        bool fahrenheit = (flags & 0x01) == 0x01;
        uint32_t mantissa = (uint32_t)x[1] | ((uint32_t)x[2] << 8) | ((uint32_t)x[3] << 16);
        int8_t exponent = x[4];
        float temperature = mantissa * pow(10, exponent);
        if (fahrenheit) {
          temperature = (temperature - 32) * 5.0 / 9.0;
        }
        id(termometer_temperature) = temperature;
        id(body_temperature).publish_state(temperature);       
      }
      return {};

  - platform: ble_client
    type: characteristic
    ble_client_id: relsib_wt50
    service_uuid: "0000180f-0000-1000-8000-00805f9b34fb"
    characteristic_uuid: "00002a19-0000-1000-8000-00805f9b34fb"
    name: "Battery Raw"
    id: batt_sensor
    internal: true
    notify: true
    lambda: |-
      if (x.size() == 1) {
        id(termometer_battery_current) = x[0];
        id(termometer_battery).publish_state(x[0]);        
      }
      return {};

  - platform: template
    id: body_temperature
    name: "Body Temperature"  
    icon: "mdi:thermometer-probe"
    unit_of_measurement: "°C"    
    accuracy_decimals: 1
    device_class: temperature
    state_class: measurement
    filters:     
      - throttle: 2s
    lambda: 'return id(termometer_temperature);'

  - platform: template
    id: termometer_battery
    name: "Termometer Battery"  
    icon: "mdi:battery"
    unit_of_measurement: "%"    
    accuracy_decimals: 0
    device_class: battery
    lambda: 'return id(termometer_battery_current);'

text_sensor:
  - platform: template
    id: termometer_status
    name: "Termometer Status"  
    icon: "mdi:thermometer-bluetooth"

  - platform: wifi_info
    ip_address:
      name: "IP Address"
    ssid:
      name: "WiFi SSID"
    mac_address:
      name: "MAC Address"

binary_sensor:
  - platform: status
    name: "Status"

  - platform: ble_presence
    mac_address: "YOUR_DEVICE_MAC_ADDRESS"
    name: "Termometer Detected"
    id: termometer_detected
                    
button:
  - platform: template
    name: "Stop Thermometer"
    id: stop_thermometer_button
    icon: "mdi:stop-circle"
    on_press:
      then:
        - lambda: |-
            id(block_until) = millis() + 210000;
            if (id(relsib_wt50)->connected()) {
              id(relsib_wt50)->disconnect();
            }
1 Like

This is a BT thermometer right??? So you added battery monitoring and presence detection to a BT thermometer??? This isnt a joke and you’re serious about this??

Yes, it’s bt thermometer. And I added battery monitoring and presence detection to a BT thermometer. This isn’t a joke and I’m serious about this (I need it for automation).

Im sure that you do! Who doesn’t do similar things with their BT thermometers?!?!

This is a medical thermometer for measuring human body temperature.