Inkbird BT thermometer iBBQ with ESP32

I think I finally have my Inkbird IBT-4XS all set up and working well now. I also had the drop outs, but I saw that ocassionally my ESP32 wouldn’t get updates for jjst over 60 seconds and so the script would set the sensor values to zero. I just changed the delay value in the script to 90s and it all works fine for me now.

I also switched to Fahrenheit by just updating the maths on each probe (example below) and also made sure that if the value was 0C then the sensor would also report 0F rather than 32F.

Full script below with the usual wifi, esp32 and ap details ommitted:

if (probe0 < 60000 and probe0 != 0) {
  id(ble_sensor_1).publish_state(probe0 / 10.0 * (9.0/5.0) + 32.0);
} else {
  id(ble_sensor_1).publish_state(0);                
}
Full ESP32 config (minus the usual wifi, ota and ap details):
esphome:
  name: bbq
  on_boot:
    priority: -10
    then:
      - lambda: |-
          {
            id(ble_sensor_1).publish_state(false);
            id(ble_sensor_2).publish_state(false);
            id(ble_sensor_3).publish_state(false);
            id(ble_sensor_4).publish_state(false); 
          }

script:
  - id: timer
    then:
      - delay: 90s
      - lambda: |-
          {
            id(ble_sensor_1).publish_state(false);
            id(ble_sensor_2).publish_state(false);        
            id(ble_sensor_3).publish_state(false);
            id(ble_sensor_4).publish_state(false); 
          }
  
esp32_ble_tracker:
  on_ble_advertise:
    - mac_address: xx:xx:xx:xx:xx
      then:
        - script.stop: timer
        - lambda: |-
            if (x.get_name() != "iBBQ") return;
            
            ESP_LOGI("ble_adv", "New BLE device");
            ESP_LOGI("ble_adv", "  address: %s", x.address_str().c_str());
            ESP_LOGI("ble_adv", "  name: %s", x.get_name().c_str());
            ESP_LOGI("ble_adv", "  Advertised service UUIDs:");
            
            for (auto uuid : x.get_service_uuids()) {
              ESP_LOGI("ble_adv", "    - %s", uuid.to_string().c_str());
            }
            
            ESP_LOGI("ble_adv", "  Advertised service data:");
            
            for (auto data : x.get_service_datas()) {
              ESP_LOGI("ble_adv", "    - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size());
            }
            
            ESP_LOGI("ble_adv", "  Advertised manufacturer data:");
            
            for (auto data : x.get_manufacturer_datas()) {
              ESP_LOGI("ble_adv", "    - %s: (%s)", data.uuid.to_string().c_str(), hexencode(data.data).c_str());
              
              if (data.uuid.contains(0, 0)) {
    
                int probe0 = (data.data[9] << 8) + data.data[8];
                int probe1 = (data.data[11] << 8) + data.data[10];
                int probe2 = (data.data[13] << 8) + data.data[12];
                int probe3 = (data.data[15] << 8) + data.data[14];

                ESP_LOGI("ble_data", "    - %f %f", probe0 / 10.0, probe1 / 10.0);
    
                if (probe0 < 60000 and probe0 != 0) {
                  id(ble_sensor_1).publish_state(probe0 / 10.0 * (9.0/5.0) + 32.0);
                } else {
                  id(ble_sensor_1).publish_state(0);                
                }
    
                if (probe1 < 60000 and probe1 != 0) {
                  id(ble_sensor_2).publish_state(probe1 / 10.0 * (9.0/5.0) + 32.0);
                } else {
                  id(ble_sensor_2).publish_state(0);                
                }
                
                if (probe2 < 60000 and probe2 != 0) {
                  id(ble_sensor_3).publish_state(probe2 / 10.0 * (9.0/5.0) + 32.0);
                } else {
                  id(ble_sensor_3).publish_state(0);                
                }
                
                if (probe3 < 60000 and probe3 != 0) {
                  id(ble_sensor_4).publish_state(probe3 / 10.0 * (9.0/5.0) + 32.0);
                } else {
                  id(ble_sensor_4).publish_state(0);                
                }
              }
            }
        - script.execute: timer
  
sensor:
  - platform: template
    name: "iBBQ Temperature Probe 1"
    id: ble_sensor_1
    unit_of_measurement: "°F"
    accuracy_decimals: 0
  - platform: template
    name: "iBBQ Temperature Probe 2"
    id: ble_sensor_2
    unit_of_measurement: "°F"
    accuracy_decimals: 0
  - platform: template
    name: "iBBQ Temperature Probe 3"
    id: ble_sensor_3
    unit_of_measurement: "°F"
    accuracy_decimals: 0
  - platform: template
    name: "iBBQ Temperature Probe 4"
    id: ble_sensor_4
    unit_of_measurement: "°F"
    accuracy_decimals: 0

Thanks for posting this. I was using the filter option on the sensor and just living with 32=0. Nice to have this little tweak!

Could it be possible to also share your automation?

Sure.

- id: '123'
  alias: Grill notification
  description: ''
  trigger:
  - platform: state
    entity_id: sensor.target_alert_temp_probe_1
    to: Alert
  - platform: state
    entity_id: sensor.target_alert_temp_probe_2
    to: Alert
  - platform: state
    entity_id: sensor.target_alert_temp_probe_3
    to: Alert
  - platform: state
    entity_id: sensor.target_alert_temp_probe_4
    to: Alert
  condition: []
  action:
  - service: notify.telegram_anton
    data:
      message: Grill needs your attention.
1 Like

good morning to me from this error if I insert the first part in the yaml of the esp32

Please remove the platform key from the [esphome] block. You’re already using the new style with the [esp32] block.

Hello I have been trying for a few days to be able to use ESP32Home with my inkbird, I installed the config file on my thermometer as described in the first topic, then I created my sensor file and the entities but it gives me an error telling me that the entities do not they have a unique id, I would like to clarify that the entities I imported from an old configuration that I had using MQTT by replacing only the sensor name.
Below I put my files and if you kindly tell me how I could solve it I would be grateful.

File Config.ymal

esphome:
  name: esp32-cucina
  on_boot:
    priority: -10
    then:
      - lambda: |-
          {
            id(ble_sensor_1).publish_state(false);
            id(ble_sensor_2).publish_state(false);        
          }
  
script:
  - id: timer
    then:
      - delay: 60s
      - lambda: |-
          {
            id(ble_sensor_1).publish_state(false);
            id(ble_sensor_2).publish_state(false);        
          }
  
esp32_ble_tracker:
  on_ble_advertise:
    - mac_address: FC:45:C3:0D:38:A8
      then:
        - script.stop: timer
        - lambda: |-
            if (x.get_name() != "iBBQ") return;
            
            ESP_LOGI("ble_adv", "New BLE device");
            ESP_LOGI("ble_adv", "  address: %s", x.address_str().c_str());
            ESP_LOGI("ble_adv", "  name: %s", x.get_name().c_str());
            ESP_LOGI("ble_adv", "  Advertised service UUIDs:");
            
            for (auto uuid : x.get_service_uuids()) {
              ESP_LOGI("ble_adv", "    - %s", uuid.to_string().c_str());
            }
            
            ESP_LOGI("ble_adv", "  Advertised service data:");
            
            for (auto data : x.get_service_datas()) {
              ESP_LOGI("ble_adv", "    - %s: (length %i)", data.uuid.to_string().c_str(), data.data.size());
            }
            
            ESP_LOGI("ble_adv", "  Advertised manufacturer data:");
            
            for (auto data : x.get_manufacturer_datas()) {
              ESP_LOGI("ble_adv", "    - %s: (%s)", data.uuid.to_string().c_str(), hexencode(data.data).c_str());
              
              if (data.uuid.contains(0, 0)) {
    
                int probe0 = (data.data[9] << 8) + data.data[8];
                int probe1 = (data.data[11] << 8) + data.data[10];
                    
                ESP_LOGI("ble_data", "    - %f %f", probe0 / 10.0, probe1 / 10.0);
    
                if (probe0 < 60000) {
                  id(ble_sensor_1).publish_state(probe0 / 10.0);
                } else {
                  id(ble_sensor_1).publish_state(0);                
                }
    
                if (probe1 < 60000) {
                  id(ble_sensor_2).publish_state(probe1 / 10.0);
                } else {
                  id(ble_sensor_2).publish_state(0);                
                }
              }
            }
        - script.execute: timer
  
sensor:
  - platform: template
    name: "iBBQ Temperatura Sonda 1"
    id: ble_sensor_1
    unit_of_measurement: "°C"
    accuracy_decimals: 0
  - platform: template
    name: "iBBQ Temperatura Sonda 2"
    id: ble_sensor_2
    unit_of_measurement: "°C"
    accuracy_decimals: 0

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "7dCYOVOF4fgpr5GAoArrfSSIxX/4fjQht7N49mLPok4="

ota:
  password: "b8df93a21ac091fb8d49f3599c3dd84d"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp32-Cucina Fallback Hotspot"
    password: "PwHj14ftDP7x"

captive_portal:

File Sensor.ymal

- platform: template
  sensors:
    owen_temp_alert:
      value_template: >-
        {% if (states.sensor.ibbq_temperatura_sonda_1.state | int) < (states.input_number.owen_temp_low.state | int) or (states.sensor.ibbq_temperatura_sonda_1.state | int)  > (states.input_number.owen_temp_high.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Owen Temp Alert
      icon_template: >-
        {% if (states.sensor.ibbq_temperatura_sonda_1.state | int) < (states.input_number.owen_temp_low.state | int) or (states.sensor.ibbq_temperatura_sonda_1.state | int)  > (states.input_number.owen_temp_high.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
      
    owen_target_temp_alert:
      value_template: >-
        {% if (states.sensor.ibbq_temperatura_sonda_1.state | int) >= (states.input_number.owen_target_temp.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Owen Target Temp Alert
      icon_template: >-
        {% if (states.sensor.ibbq_temperatura_sonda_1.state | int) >= (states.input_number.owen_target_temp.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
            
    meat_target_temp_alert:
      value_template: >-
        {% if (states.sensor.ibbq_temperatura_sonda_2.state | int) >= (states.input_number.meat_target_temp.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Meat Target Temp Alert
      icon_template: >-
        {% if (states.sensor.ibbq_temperatura_sonda_2.state | int) >= (states.input_number.meat_target_temp.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
      
    meat_temp_alert:
      value_template: >-
        {% if (states.sensor.ibbq_temperatura_sonda_2.state | int) < (states.input_number.meat_temp_low.state | int) or (states.sensor.ibbq_temperatura_sonda_2.state | int)  > (states.input_number.meat_temp_high.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Meat Temp Alert
      icon_template: >-
        {% if (states.sensor.ibbq_temperatura_sonda_2.state | int) < (states.input_number.meat_temp_low.state | int) or (states.sensor.ibbq_temperatura_sonda_2.state | int)  > (states.input_number.meat_temp_high.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}

I believe nothing is missing

Thanks for sharing your experiences. I had a similar situation - an IBT-2x that is being reconginzed as xBBQ publishing temperature measurements with UUID 0x0001. The changes are (obviously) two:

  1. Recognizing the device as xBBQ:
if (x.get_name() != "xBBQ") return;
  1. Manufacturer datas for the temperature reading UUID is different:
if (data.uuid.contains(1, 0)) {

@paddy0174 Could you perhaps add a note to your original post about this variation as it is obviously getting lost down there? (btw. I’ve figured out the two changes by myself prior to finding the relevant solution already discussed here)

I might be missing something, but Inkbird thermometers are now directly supported as an integration:

If you want to use an ESP32 to extend the range, you should be able to just use the new bluetooth_proxy component in ESPHome:

So instead of all the complicated stuff in the original post. Just adding “bluetooth_proxy:” to your ESPHome config should do the trick, and the sensors should automatically be discovered.

good morning how is it possible to read the degrees even after the . like 29.2°C and not just 29?

I added precision_decimals: 1

to my sensor configuration

but it gives me this error

Invalid config for [sensor.template]: [accuracy_decimals] is an invalid option for [sensor.template]. Check: sensor.template->sensors->owen_temp_alert->accuracy_decimals. (See ?, line ?).

under my configuration

- platform: template
  sensors:
    owen_temp_alert:
      value_template: >-
        {% if (states.sensor.ibt_2x_tempc.state | int) < (states.input_number.owen_temp_low.state | int) or (states.sensor.ibt_2x_tempc.state | int)  > (states.input_number.owen_temp_high.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Owen Temp Alert
      icon_template: >-
        {% if (states.sensor.ibt_2x_tempc.state | int) < (states.input_number.owen_temp_low.state | int) or (states.sensor.ibt_2x_tempc.state | int)  > (states.input_number.owen_temp_high.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
      accuracy_decimals: 1    

      
    owen_target_temp_alert:
      value_template: >-
        {% if (states.sensor.ibt_2x_tempc.state | int) >= (states.input_number.owen_target_temp.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Owen Target Temp Alert
      icon_template: >-
        {% if (states.sensor.ibt_2x_tempc.state | int) >= (states.input_number.owen_target_temp.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
      accuracy_decimals: 1
            
    meat_target_temp_alert:
      value_template: >-
        {% if (states.sensor.ibt_2x_tempc2.state | int) >= (states.input_number.meat_target_temp.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Meat Target Temp Alert
      icon_template: >-
        {% if (states.sensor.ibt_2x_tempc2.state | int) >= (states.input_number.meat_target_temp.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
      accuracy_decimals: 1
      
    meat_temp_alert:
      value_template: >-
        {% if (states.sensor.ibt_2x_tempc2.state | int) < (states.input_number.meat_temp_low.state | int) or (states.sensor.ibt_2x_tempc2.state | int)  > (states.input_number.meat_temp_high.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Meat Temp Alert
      icon_template: >-
        {% if (states.sensor.ibt_2x_tempc2.state | int) < (states.input_number.meat_temp_low.state | int) or (states.sensor.ibt_2x_tempc2.state | int)  > (states.input_number.meat_temp_high.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
      accuracy_decimals: 1

Well. You are converting everything to integers using “| int” in your templates, so there will be no decimals.

You need to use floating point (| float) and the “round” function (| round(1)) in order to get what you want.

Also - I do not believe “accuracy_decimals” is valid for a platform sensor.

would you help make me understand with some example in my setup

hello I tried to modify my config adding your configurations but it gives me this error

Invalid config for [sensor.template]: [grill_alert_low] is an invalid option for [sensor.template]. Check: sensor.template->sensors->grill_alert_high->grill_alert_low. (See ?, line ?).

- platform: template
  sensors:
  
    owen_temp_alert:
      value_template: >-
        {% if (states.sensor.ibt_2x_tempc.state | int) < (states.input_number.owen_temp_low.state | int) or (states.sensor.ibt_2x_tempc.state | int)  > (states.input_number.owen_temp_high.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Owen Temp Alert
      icon_template: >-
        {% if (states.sensor.ibt_2x_tempc.state | int) < (states.input_number.owen_temp_low.state | int) or (states.sensor.ibt_2x_tempc.state | int)  > (states.input_number.owen_temp_high.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
        
    owen_target_temp_alert:
      value_template: >-
        {% if (states.sensor.ibt_2x_tempc.state | int) >= (states.input_number.owen_target_temp.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Owen Target Temp Alert
      icon_template: >-
        {% if (states.sensor.ibt_2x_tempc.state | int) >= (states.input_number.owen_target_temp.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
        
    meat_target_temp_alert:
      value_template: >-
        {% if (states.sensor.ibt_2x_tempc2.state | int) >= (states.input_number.meat_target_temp.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Meat Target Temp Alert
      icon_template: >-
        {% if (states.sensor.ibt_2x_tempc2.state | int) >= (states.input_number.meat_target_temp.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
        
    meat_temp_alert:
      value_template: >-
        {% if (states.sensor.ibt_2x_tempc2.state | int) < (states.input_number.meat_temp_low.state | int) or (states.sensor.ibt_2x_tempc2.state | int)  > (states.input_number.meat_temp_high.state | int) %}
          Alert
        {% else %}
          Normal
        {% endif %}
      friendly_name: Meat Temp Alert
      icon_template: >-
        {% if (states.sensor.ibt_2x_tempc2.state | int) < (states.input_number.meat_temp_low.state | int) or (states.sensor.ibt_2x_tempc2.state | int)  > (states.input_number.meat_temp_high.state | int) %}
          mdi:alert-circle
        {% else %}
          mdi:alert-circle-check
        {% endif %}
        
    grill_alert_low:
        friendly_name: "Sonda 1 Low Temp"
        unit_of_measurement: "ºC"
        value_template: "{{ (((states('input_number.owen_temp_low') | float)))|round(1)}}"
    grill_alert_high:
        friendly_name: "Sonda 1 High Temp"
        unit_of_measurement: "ºC"
        value_template: "{{ (((states('input_number.owen_temp_high') | float)))|round(1)}}"
        grill_alert_low:
         friendly_name: "Sonda 2 Low Temp"
         unit_of_measurement: "ºC"
         value_template: "{{ (((states('input_number.meat_temp_low') | float)))|round(1)}}"
        grill_alert_high:
         friendly_name: "Sonda 2 High Temp"
         unit_of_measurement: "ºC"
         value_template: "{{ (((states('input_number.meat_temp_high') | float)))|round(1)}}"

I understand that grill_alert_low is repeated twice but it’s the two probes how can I do for the two probes?

Thank you @aherbjornsen . Wow so easy and quick to setup with Bluetooth Proxy.
Is it possible to obtain the Inkbird battery level?

Were you able to get the battery status coming across for the Inkbird. If so would you mind sharing. I am running a IBT-4XS. Thanks

Thanks for this, it was driving me nuts. I totally missed that typo in the tutorial.

I had the bluetooth proxy in passive mode, but it couldn’t see my IBT-4XS.
I have changed to use active mode and HA found it immediately.

can you share automation?