Inkbird INT-11I-B thermometer integration for ESPHome

For anyone who needs to integrate this thermometer model, I was able to extract some data: the probe temperature, along with the battery charge level of both the probe and the booster.

You only need to change the MAC address value to that of your device.

VERY IMPORTANT!
Set an initial recipe on your device through the official app. Without this setting, the booster turns off after 10 minutes.

###   This is a package related to the Inkbird INT-11I-B thermometer.
###   The code acquires the probe temperature data and the battery status (for both the booster and the probe).
###   It also provides the connection status of the probe and the booster.
###   Replace the MAC address with that of your own device.
###   VERY IMPORTANT! Set an initial recipe on your device through the official app. Without this setting, the booster turns off after 10 minutes.


substitutions:
  inkbird_mac: "00:00:00:00:00:00" #### REPLACE WITH YOUR DEVICE MAC

esphome:
  name: inkbird
  friendly_name: Inkbird

  on_boot:
    priority: 200.0
    then:
      - lambda: |-
          // Stato iniziale: "scollegati" finché non arrivano dati
          id(inkbird_last_valid_temp_ms) = 0;
          id(inkbird_last_station_batt_ms) = 0;
          id(inkbird_station_connected).publish_state(false);
          id(inkbird_probe_connected).publish_state(false);

esp32:
  board: esp32dev
  framework:
    type: esp-idf

logger:

globals:
  # Timestamp ultimo valore valido di temperatura (sonda)
  - id: inkbird_last_valid_temp_ms
    type: uint32_t
    restore_value: no
    initial_value: '0'

  # Timestamp ultimo valore ricevuto di batteria stazione base
  - id: inkbird_last_station_batt_ms
    type: uint32_t
    restore_value: no
    initial_value: '0'

binary_sensor:
  - platform: template
    name: "Connessione Base"
    id: inkbird_station_connected
    device_class: connectivity

  - platform: template
    name: "Connessione Sonda"
    id: inkbird_probe_connected
    device_class: connectivity

api:
  encryption:
    key: "yourkey"

ota:
  - platform: esphome
    password: "yourpassword"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
#  manual_ip:
#    static_ip: 192.168.1.55
#    gateway: 192.168.1.254
#    subnet: 255.255.255.0
#    dns1: 192.168.1.254

time:
  - platform: sntp
    id: ntp_time
    timezone: Europe/Rome

interval:
  - interval: 5s
    then:

      - lambda: |-
          // Watchdog 2 minuti
          const uint32_t now = millis();

          const bool base_ok =
            (id(inkbird_last_station_batt_ms) != 0) &&
            (now - id(inkbird_last_station_batt_ms) < 120000);  // 2 min

          // Se la base non è ok, la sonda è forzata OFF
          bool probe_ok = false;
          if (base_ok) {
            probe_ok =
              (id(inkbird_last_valid_temp_ms) != 0) &&
              (now - id(inkbird_last_valid_temp_ms) < 120000);  // 2 min
          }

          if (id(inkbird_station_connected).state != base_ok) {
            id(inkbird_station_connected).publish_state(base_ok);
          }
          if (id(inkbird_probe_connected).state != probe_ok) {
            id(inkbird_probe_connected).publish_state(probe_ok);
          }

ble_client:
  - mac_address: ${inkbird_mac}
    id: inkbird
    auto_connect: true

    on_connect:
      then:
        - lambda: |-
            ESP_LOGI("inkbird", "Connesso a ${inkbird_mac}");
            //inkbird_on_connect();

    on_disconnect:
      then:
        - lambda: |-
            ESP_LOGW("inkbird", "Disconnesso – auto_connect si riconnette automaticamente");
            // Non forziamo qui i sensori: decide tutto il watchdog a 2 minuti

sensor:
  - platform: template
    name: "Temperatura"
    id: inkbird_temperature
    unit_of_measurement: '°C'
    device_class: temperature
    accuracy_decimals: 1
    update_interval: never

  - platform: ble_client
    type: characteristic
    ble_client_id: inkbird
    internal: true
    id: inkbird_internal_raw
    service_uuid: 0000FF00-0000-1000-8000-00805F9B34FB
    characteristic_uuid: 0000FF01-0000-1000-8000-00805F9B34FB
    unit_of_measurement: '°C'
    accuracy_decimals: 1
    notify: true
    lambda: |-
      char hex_buf[128];
      int pos = 0;
      for (size_t i = 0; i < x.size() && pos < (int)sizeof(hex_buf) - 4; i++) {
        pos += snprintf(hex_buf + pos, sizeof(hex_buf) - pos,
                        "%02X%s", x[i], (i + 1 < x.size() ? "-" : ""));
      }
      ESP_LOGW("INKBIRD_RAW", "FF01: %s", hex_buf);

      if (x.size() >= 2) {
        uint16_t raw_fahrenheit = (x[1] << 8) | x[0];
        float fahrenheit = raw_fahrenheit / 100.0f;
        float celsius    = (fahrenheit - 32.0f) * 5.0f / 9.0f;
        float celsius_r  = roundf(celsius * 10.0f) / 10.0f;
        ESP_LOGI("TEMP_DECODER", "F: %.2f -> C: %.1f", fahrenheit, celsius_r);
        return celsius_r;
      }
      return NAN;

    on_value:
      then:
        - lambda: |-
            const float t = id(inkbird_internal_raw).state;

            // "Valido" = non NaN e range plausibile
            if (!isnan(t) && t >= -40.0f && t <= 100.0f) {
              id(inkbird_temperature).publish_state(t);

              // Timestamp ultima temperatura valida
              id(inkbird_last_valid_temp_ms) = millis();

              // Se la base risulta connessa, accendiamo subito la sonda (senza aspettare l'interval)
              if (id(inkbird_station_connected).state && !id(inkbird_probe_connected).state) {
                id(inkbird_probe_connected).publish_state(true);
              }
            }

  - platform: template
    id: inkbird_battery_station
    name: "Batteria Stazione"
    unit_of_measurement: "%"
    device_class: battery
    accuracy_decimals: 0

  - platform: template
    id: inkbird_battery_probe
    name: "Batteria Sonda"
    unit_of_measurement: "%"
    device_class: battery
    accuracy_decimals: 0

  - platform: ble_client
    type: characteristic
    ble_client_id: inkbird
    internal: true
    id: inkbird_battery_raw
    service_uuid: 0000FF00-0000-1000-8000-00805F9B34FB
    characteristic_uuid: 00002A19-0000-1000-8000-00805F9B34FB
    update_interval: 1min
    notify: false
    lambda: |-
      if (x.size() < 2) return NAN;

      char buf[64];
      int pos = 0;
      for (size_t i = 0; i < x.size() && pos < (int)sizeof(buf)-4; i++) {
        pos += snprintf(buf + pos, sizeof(buf) - pos, "%02X%s", x[i], (i+1<x.size() ? " " : ""));
      }
      ESP_LOGD("INKBIRD_BATT", "Battery raw: %s", buf);

      uint8_t batt_station = x[0];
      uint8_t batt_probe   = x[1];

      ESP_LOGI("INKBIRD_BATT", "Stazione: %u%%, Sonda: %u%%", batt_station, batt_probe);

      // Pubblica batteria stazione + aggiorna "connessione base" se valido
      if (batt_station >= 1 && batt_station <= 100) {
        id(inkbird_battery_station).publish_state((float)batt_station);

        // Timestamp ultimo pacchetto batteria base valido
        id(inkbird_last_station_batt_ms) = millis();

        // Accendi subito la base (senza aspettare l'interval)
        if (!id(inkbird_station_connected).state) {
          id(inkbird_station_connected).publish_state(true);
        }
      }

      // Pubblica batteria sonda (solo informativo, NON usato per connettività sonda)
      if (batt_probe >= 1 && batt_probe <= 100) {
        id(inkbird_battery_probe).publish_state((float)batt_probe);
      }

      return (float)batt_station;
1 Like