Support for MEATER true wireless cooking thermometer

I have a Meater+ ( reporting firmware ‘04|01.06.00.83’ )
The temperature reporting seems to be quite OK - compared with a Weber thermometer, differences are within what I would expect given the two probes are not inserted in the exact same location in the meat.

What I notice is that the “block” is blinking green - probably meaning it is waiting for connection.
I have it lying in room temperature now, let’s see how long it lasts before shutdown. I am guessing an hour before I need to re-insert it into the block and take it out again. Somewhat inconvenient when using for steaks left in the oven or grill for hours :smirk:

Some highlights from the logging:

[11:42:50][C][ble_sensor:018]:   MAC address        : B8:1F:5E:84:72:30
[11:42:50][C][ble_sensor:019]:   Service UUID       : A75CC7FC-C956-488F-AC2A-2DBC08B63A04
[11:42:50][C][ble_sensor:020]:   Characteristic UUID: 7EDDA774-045E-4BBF-909B-45D1991A2876
[11:42:50][C][ble_sensor:021]:   Descriptor UUID    : 00000000-0000-0000-0000-000000000000

[11:42:59][D][text_sensor:064]: 'Meater - Firmware': Sending state '04|01.06.00.83'
[11:42:59][D][sensor:094]: 'firmware': Sending state 14.00000  with 0 decimals of accuracy

[11:47:51][D][sensor:094]: 'Meater - Battery level': Sending state 100.00000 % with 0 decimals of accuracy
[11:47:53][D][sensor:094]: 'Meater - Probe': Sending state 27.00000 °C with 1 decimals of accuracy
[11:47:53][D][sensor:094]: 'Meater - Ambient': Sending state 27.00000 °C with 1 decimals of accuracy

Yes - pretty much an hour:
Meater - Uptime [s] 1:04:01

[12:28:15][D][esp-idf:000]: W (3910558) BT_APPL: gattc_conn_cb: if=3 st=0 id=3 rsn=0x8
[12:28:15][D][esp-idf:000]: W (3910562) BT_HCI: hcif disc complete: hdl 0x1, rsn 0x8
[12:28:15][W][ble_sensor:037]: [Meater - Probe] Disconnected!
[12:28:16][D][esp32_ble_tracker:266]: Starting scan...
[12:28:47][W][ble_sensor:117]: [Meater - Probe] Cannot poll, not connected

Does this work with the meater 2 plus?

1 Like

Thanks to your ESPHome yaml I got this up and running in no time. You wouldn’t happen to know how to easily change it to Fahrenheit?

1 Like

If anyone else is wondering just add a filter to convert to Fahrenheit. Same level as Platform.

filters:
      - lambda: return x * (9.0/5.0) + 32.0;
1 Like

I just got mine set up and would also like to switch to °F.
Where exactly did you add this to your Yaml? I would Greatly appreciate this.

Nevermind I’ve got it. thank you for providing the solution.

Thanks, it is working great with meater+
I added some more information befor a connecton could be made. It was according to HA documentation.

Appreciate the work put in by everyone in the thread to reverse engineer the Meater BLE protocol.

I made some improvements to the ESPHome code above, so I thought I’d share them. The below just needs to be copied into the config of any ESPHome device with Bluetooth capabilities that is near where you’ll be using your Meater. Be sure to insert your device’s MAC address. I found it pretty straightforward to just use the LightBlue app. It prints out a list of nearby Bluetooth devices and their MAC addresses, so just grab the one that says MEATER.

The code above had the temperature values as integers, but then returned 2 decimal places, so the temperature would go from e.g. 70.00 to 71.00. I changed them to floats and reduced to 1 decimal place, as that’s really the limit of precision of this thing.

I also added device_class and state_class to each sensor, which allows Home Assistant to automatically convert units to your defaults. So no need to mess with the temperature calculation math.

I removed some icons and casts to (float), as they seemed extraneous. Not overriding the icon also allows Home Assistant to customize icons in some cases (e.g. if the battery entity is unavailable you’ll get the battery + question mark icon).

As others have mentioned, Meater doesn’t seem to be able to handle the tip temperature being higher than the ambient temperature, so just a reminder if anyone else tests this by putting the tip in some boiling water.

I didn’t however, experience the Meater powering off/disconnecting after an hour. Mine will seemingly keep transmitting until the battery dies. It transmitted for 18 hours overnight, though there were two ~1hr interruptions after ~7hrs and ~12hrs in. Not sure if that’s the device or my ESPHome device disconnecting.
My device is the Meater Plus, with probe MT-PR10 and charger MT-CP01.

ble_client:
  - mac_address: **insert your Meater's MAC address here**
    id: meater

text_sensor:
  - platform: template
    name: "Meater Firmware"
    id: meater_firmware

sensor:
  - name: "Meater Tip Temperature"
    platform: ble_client
    type: characteristic
    ble_client_id: meater
    service_uuid: 'a75cc7fc-c956-488f-ac2a-2dbc08b63a04'
    characteristic_uuid: '7edda774-045e-4bbf-909b-45d1991a2876'
    unit_of_measurement: '°C'
    accuracy_decimals: 1
    device_class: "temperature"
    state_class: "measurement"
    notify: true
    lambda: |-
      float tip_temp = (x[0] + (x[1] << 8) + 8.0) / 16.0;
      return tip_temp;
  - name: "Meater Ambient Temperature"
    platform: ble_client
    type: characteristic
    ble_client_id: meater
    service_uuid: 'a75cc7fc-c956-488f-ac2a-2dbc08b63a04'
    characteristic_uuid: '7edda774-045e-4bbf-909b-45d1991a2876'
    unit_of_measurement: '°C'
    accuracy_decimals: 1
    device_class: "temperature"
    state_class: "measurement"
    notify: true
    lambda: |-
      uint16_t tip = x[0] + (x[1] << 8);
      uint16_t ra = x[2] + (x[3] << 8);
      uint16_t oa = x[4] + (x[5] << 8);
      uint16_t min_val = 48;
      float ambient = (tip + std::max(0., (((ra - std::min(min_val, oa)) * 16. * 589.) / 1487.)) + 8.0) / 16.;
      return ambient;
  - name: "Meater Battery Level"
    platform: ble_client
    type: characteristic
    ble_client_id: meater
    service_uuid: 'a75cc7fc-c956-488f-ac2a-2dbc08b63a04'
    characteristic_uuid: '2adb4877-68d8-4884-bd3c-d83853bf27b8'
    unit_of_measurement: '%'
    device_class: "battery"
    state_class: "measurement"
    notify: true
    lambda: |-
      uint16_t battery = (x[0] + x[1]) * 10;
      return battery;
  - name: "Meater RSSI"
    platform: ble_client
    type: rssi
    ble_client_id: meater
    device_class: "signal_strength"
    state_class: "measurement"
  - id: firmware
    platform: ble_client
    type: characteristic
    ble_client_id: meater
    service_uuid: '180A'
    characteristic_uuid: '00002a26-0000-1000-8000-00805f9b34fb'
    lambda: |-
      std::string data_string(x.begin(), x.end());
      id(meater_firmware).publish_state(data_string.c_str());
      return x.size();

3 Likes

Followed this and worked perfectly. I flashed by wire each time just to ensure any partition re-writing that is needed would be done. Thanks for getting this all figured out!

Hello together,

has anybody found a solution regaring the problem, that the connection after a while is lost? On my side it is nearly a hour.

Hi @StevG,

I have a Meater+. I used the code above to implement the bluetooth client. If i view the data in home assistant history, it collected temperature readings for almost 48hrs on a single charge.

I had a similar issue at first using a Plotly graph card on a dashboard where it would only show the last hour. Something about the hours_to_show was clipping the data start time.

:man_shrugging:

I think I have an Idea, why it is stopping. Which Bluetooth Connection do you use. To the Meater+ or to the Meater Probe?

I tried to use the HeX of the meater probe not of the meater+. That is the right way. Currently the meater is sending data more than 24 hours.

We tried this yesterday. Its been on in my cooler over night with no problem. Its connected to the Meater+, not directly to the probe and feeds the data to home assistant.

We also added:

captive_portal:
bluetooth_proxy:

Just for some extra funktionality on the same device.

What would be good is a pass through so that the esp32 presents itself as a Meater+ to the app. Then one could use the app-cooks while still getting the temps locally in HA.

1 Like

I have a Meater2 Pro (MT-PR20/MT-CP20) and the code earlier in the thread does not work for that. I dug around a bit and here’s code for that if anyone is looking. Still haven’t tested enough to say if the battery % is correct, but temp seems good.

esphome:
  name: meater-proxy
  friendly_name: Meater Proxy

esp32:
  board: mhetesp32minikit
  framework:
    type: arduino

globals:
  - id: meater_payload
    type: std::vector<uint8_t>
    restore_value: no
    initial_value: 'std::vector<uint8_t>()'

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: ***

ota:
  - platform: esphome
    password: ***

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

esp32_ble_tracker:

ble_client:
  - mac_address: XX:XX:XX:XX:XX:XX
    id: meater

text_sensor:
  - platform: template
    name: "Meater Firmware"
    id: meater_firmware
  - platform: template
    name: "Meater Raw Payload"
    id: meater_raw_payload
    internal: True

sensor:
  - platform: ble_client
    type: characteristic
    ble_client_id: meater
    name: "Meater Raw Payload Trigger"
    id: meater_raw_trigger
    internal: true
    service_uuid: 'dcbb67ca-64fb-41a3-99d1-5d9fd8cf33ca'
    characteristic_uuid: '7edda774-045e-4bbf-909b-45d1991a2876'
    notify: true
    lambda: |-
      id(meater_payload) = x;  // Save payload globally

      std::string hex;
      for (auto b : x) {
        char buf[4];
        sprintf(buf, "%02X ", b);
        hex += buf;
      }
      id(meater_raw_payload).publish_state(hex);
      return x.size();  // Dummy return
  - platform: template
    name: "Meater Tip 1 Temperature"
    lambda: |-
      auto &x = id(meater_payload);
      if (x.size() < 12) return NAN;

      return (x[0] + (x[1] << 8) + 4.0) / 32.0;
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    device_class: "temperature"
    state_class: "measurement"

  - platform: template
    name: "Meater Tip 2 Temperature"
    lambda: |-
      auto &x = id(meater_payload);
      if (x.size() < 12) return NAN;
      return (x[2] + (x[3] << 8) + 4.0) / 32.0;
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    device_class: "temperature"
    state_class: "measurement"

  - platform: template
    name: "Meater Tip 3 Temperature"
    lambda: |-
      auto &x = id(meater_payload);
      if (x.size() < 12) return NAN;
      return (x[4] + (x[5] << 8) + 4.0) / 32.0;
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    device_class: "temperature"
    state_class: "measurement"

  - platform: template
    name: "Meater Tip 4 Temperature"
    lambda: |-
      auto &x = id(meater_payload);
      if (x.size() < 12) return NAN;
      return (x[6] + (x[7] << 8) + 4.0) / 32.0;
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    device_class: "temperature"
    state_class: "measurement"

  - platform: template
    name: "Meater Tip 5 Temperature"
    lambda: |-
      auto &x = id(meater_payload);
      if (x.size() < 12) return NAN;
      return (x[8] + (x[9] << 8) + 4.0) / 32.0;
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    device_class: "temperature"
    state_class: "measurement"

  - platform: template
    name: "Meater Ambient Temperature"
    lambda: |-
      auto &x = id(meater_payload);
      if (x.size() < 12) return NAN;
      return (x[10] + (x[11] << 8) + 4.0) / 32.0;
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    device_class: "temperature"
    state_class: "measurement"

  - name: "Meater Battery Level"
    platform: ble_client
    type: characteristic
    ble_client_id: meater
    service_uuid: 'dcbb67ca-64fb-41a3-99d1-5d9fd8cf33ca'
    characteristic_uuid: '2adb4877-68d8-4884-bd3c-d83853bf27b8'
    unit_of_measurement: '%'
    device_class: "battery"
    state_class: "measurement"
    notify: true
    lambda: |-
      uint8_t battery = x[0];
      return battery;
  - name: "Meater RSSI"
    platform: ble_client
    type: rssi
    ble_client_id: meater
    device_class: "signal_strength"
    state_class: "measurement"
  - id: firmware
    platform: ble_client
    type: characteristic
    ble_client_id: meater
    service_uuid: '180A'
    characteristic_uuid: '00002a26-0000-1000-8000-00805f9b34fb'
    lambda: |-
      std::string data_string(x.begin(), x.end());
      id(meater_firmware).publish_state(data_string.c_str());
      return x.size();