I recently purchsed a bbq grill and wanted to use a thermometer, that I can integrate in HA without much hassle. As it turns out, there is not yet an easy solution to use with already existing components. So I did some searching and found a few tutorials, that integrate the Inkbird Bluetooth thermometers into HA, but none of them use components like ESPHome (and it’s BLE stack).
So I decided to try my luck and see, if there isn’t another way of integrating these devices into HA. And here it goes!
What do you get here?
The possibility to integrate an Inkbird BLE thermometer into HA with an ESP32 running on ESPHome. No MQTT or other services are needed, we are just using the BLE connection and the native HA api in ESPHome.
What do I need?
An Inkbird BLE thermometer like IBT-2x, IBT-4x, IBT-6x or IBT-8x and an ESP32 board. The ESP8266 is not suitable, because it doesn’t have a BT controller.
How does this work?
Let’s start with some technical background. The Inkbird thermometers connect to an app on your phone via BT. This is the normal way these thermometers are used. Unfortunately this leaves us with a few problems like BT range, transfer of the sensor data to HA and the need for an app.
The solution is, we use an ESP32 board to readout the BT data that the thermometer provides without any connection, app or whatsoever and setup some sensors to push the data to HA via the ESPHome<->HA api.
Yaml for the node (the ESPHome part):
esphome:
name: flora_livingroom_window
platform: ESP32
board: nodemcu-32s
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: 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];
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 Temperature Probe 1"
id: ble_sensor_1
unit_of_measurement: "°C"
accuracy_decimals: 0
- platform: template
name: "iBBQ Temperature Probe 2"
id: ble_sensor_2
unit_of_measurement: "°C"
accuracy_decimals: 0
This is stripped down to the essentials. You still need the “standards” like wifi
, ap
, captive_portal
, logger
and so on. All the things you normally set as well.
Let’s break this down into pieces to see what we do here:
esphome:
name: flora_livingroom_window
platform: ESP32
board: nodemcu-32s
on_boot:
priority: -10
then:
- lambda: |-
{
id(ble_sensor_1).publish_state(false);
id(ble_sensor_2).publish_state(false);
}
This is our description of the used ESP board. Just for safety reasons, we set the sensors (more on them later) to “0” at the start of the ESP. The priority is set to “-10” as resetting the sensors doesn’t need any priority at all.
esp32_ble_tracker:
on_ble_advertise:
- mac_address: 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());
}
First we implement the esp32_ble_tracker
to get the BT functionality into ESPHome. If our ESP get’s any advertised data (from any of your BT devices, not only the thermometer), it starts to first check the mac address of your thermometer. If the mac address fits our device, we move on and start a timer script (more on that later).
Our next step is to check for the correct name (is this an Inkbird Thermometer?) and write some information to the ESP log screen (Hint, this is where you find the mac address the first time you run the ESP).
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
Here we start to look at the so called “manufacturer_data”, this is where our thermometer sends the actual values of the thermometer probes. If the value is over 60000, that’s the way for the thermometer to tell, there is no value… As the sent values are off by a factor of ten, we need to adjust these (hence the division by ten). In the end we start our timer script.
script:
- id: timer
then:
- delay: 60s
- lambda: |-
{
id(ble_sensor_1).publish_state(false);
id(ble_sensor_2).publish_state(false);
}
This is just a simple script, that sets our temperature sensors back to “0”, if the thermometer isn’t sending any data for x seconds (in this case 60s). If the thermometer sends a new data package within this x seconds, the script get’s stopped and is started again after receiving the new values.
sensor:
- platform: template
name: "iBBQ Temperature Probe 1"
id: ble_sensor_1
unit_of_measurement: "°C"
accuracy_decimals: 0
- platform: template
name: "iBBQ Temperature Probe 2"
id: ble_sensor_2
unit_of_measurement: "°C"
accuracy_decimals: 0
And lastly, we have our sensor
s for HA. These were set in the “main magic” above and now just need to be definied. The accuracy_decimals
are set to “0”, because the thermometer does only report values without decimals.
The above code works fine for the IBT-2 with two probes, but what if you have more than that, eg. four, six or eight? See the details below on what you have to change, if you use more than two probes.
Instructions for adding more probes
Very simply spoken, you just have to add more probes to all the already there ones. Here are the lines you have to add or change.
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); # <- add one new line for every probe you have and increment the number of the sensor by one
id(ble_sensor_4).publish_state(false);
}
script:
- id: timer
then:
- delay: 60s
- lambda: |-
{
id(ble_sensor_1).publish_state(false);
id(ble_sensor_2).publish_state(false);
id(ble_sensor_3).publish_state(false); # <- same here, add one new line for every probe you have and increment the number of the sensor by one
id(ble_sensor_4).publish_state(false);
}
if (data.uuid.contains(0, 0)) {
int probe0 = (data.data[9] << 8) + data.data[8];
int probe1 = (data.data[11] << 8) + data.data[10]; # <- here you have to duplicate one line and increment the values for the probe name by one and the data by two (!)
int probe2 = (data.data[13] << 8) + data.data[12];
int probe3 = (data.data[15] << 8) + data.data[14]; # <- NOTE: the name for the probes counts from zero, not from one!
ESP_LOGI("ble_data", " - %f %f %f %f", probe0 / 10.0, probe1 / 10.0, probe2 / 10.0, probe3 / 10.0); # <- here we need to add one "%f" for every new probe and divide every new probe by ten
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);
}
# Every new probe needs it's own if statement
# NOTE: as above, take note of the incrementation of the probes vs the ble_sensors. Probes start with zero, ble_sensors with 1!
if (probe2 < 60000) {
id(ble_sensor_3).publish_state(probe2 / 10.0);
} else {
id(ble_sensor_3).publish_state(0);
}
if (probe3 < 60000) {
id(ble_sensor_4).publish_state(probe1 / 10.0);
} else {
id(ble_sensor_4).publish_state(0);
}
}
sensor:
- platform: template
name: "iBBQ Temperature Probe 1"
id: ble_sensor_1
unit_of_measurement: "°C"
accuracy_decimals: 0
- platform: template
name: "iBBQ Temperature Probe 2"
id: ble_sensor_2
unit_of_measurement: "°C"
accuracy_decimals: 0
- platform: template
name: "iBBQ Temperature Probe 3" # <- Increment the probe by one
id: ble_sensor_3 # <- Increment the ble_sensor by one
unit_of_measurement: "°C"
accuracy_decimals: 0
- platform: template
name: "iBBQ Temperature Probe 4"
id: ble_sensor_4
unit_of_measurement: "°C"
accuracy_decimals: 0
You’re done replicating. If you have the six or eight probe version, just do the steps again and increment the numbers accordingly.
You can always add additional things to this ESP like other temperature or humidity sensors, or the control of a LED strip. But keep in mind, that the BLE stack used by ESPHome is not small. If you add other things, you can run into re-boots of the ESP. My experience is, if you don’t put a lot of stress on the ESP board, you can still use some other stuff. In my case the ESP is running the code above and additionally I use it for three of my self-made plant watering sensors. It fit’s perfectly, because the flowers (and their sensors) are standing on my living room windowsills, and the BBQ is right on the other side of the window.
How to get the mac address?
On the first run of your ESP you might not know the actual mac address of your new thermometer. This is not a problem, as our ESP is scanning for all devices. Just follow the instructions beneath and you’ll get the mac address and can add it after the first run.
How to use this?
1.) Setup a new node in ESPHome, take care of the correct board type
2.) Open the yaml file for that node. Your settings for wifi
, ap
and so on need to be set
3.) Copy now the on_boot
part from the code into your yaml file
4.) The three parts esp32_ble_tracker
, script
and sensor
must be completly copied and pastd in your yaml file. If you already have a part for eg. sensor
, just add the sensors to this, like so:
add sensors (taken from my above mentioned plant sensors
sensor:
- platform: template
name: "iBBQ Temperature Probe 1"
id: ble_sensor_1
unit_of_measurement: "°C"
accuracy_decimals: 0
- platform: template
name: "iBBQ Temperature Probe 2"
id: ble_sensor_2
unit_of_measurement: "°C"
accuracy_decimals: 0
- platform: adc
pin: GPIO33
name: "Flora Livingroom Window Rubber Tree"
attenuation: 11db
unit_of_measurement: '%'
accuracy_decimals: 0
update_interval: 300sec
filters:
- calibrate_linear:
# Map 0.0 (from sensor) to 0.0 (true value)
- 2.76 -> 0.0
- 1.08 -> 100.0
- median:
window_size: 7
send_every: 4
send_first_at: 2
5.) Delete the line with the mac address, like so
on_ble_advertise:
- then:
6.) Switch your thermometer “on”
7.) Save and upload the yaml to your ESP node
8.) After uploading and restarting the node, wait a bit and look for the mac address in the log entries.
9.) Go back to edit the node and add the line for the mac_address
back to your yaml. Save, upload and restart the node.
10.) You’re done, have fun with your newly integrated Inkbird BBQ thermometer.
11.) You can use these sensors in all kind of automations or notifications or where ever you need to in HA
These instructions are heavily based on the script from VanFlipsen found in the still open request to add Inkbird sensors natively to ESPHome on Github. Thanks!
There is one drawback at the moment, and that is the battery power readout. The problem is, the temperatures are sent automatically via BT by the thermometer, so we can grab them without any authentication or sending commands to the thermometer. Unfortunately the battery isn’t sent automatically, so the thermometer must be polled. Right now I haven’t found an easy way to set this up in ESPHome, but I’ll watch the possibilities closely and will update this thread, if something comes up.
Right now it’s just the temperatures, but for me it’s enough for the moment. If batteries run out, I always have AAA batteries around.
Have fun!