core icon indicating copy to clipboard operation
core copied to clipboard

Inkbird IBS-P01B bluetooth pool thermometer only reports new max/min temperatures and not the current temperature

Open DavidCEllis opened this issue 7 months ago • 18 comments

The problem

The setup

Home Assistant 2025.4.2 ESPHome Bluetooth Proxy - Firmware: 25.3.1 (ESPHome 2025.2.2) Inkbird IBS-P01B Pool Thermometer

The issue

Initially the thermometer appeared to be working correctly and gathering temperature data but gradually it changed until it only reported new maximum or minimum values.

I can look in the advertisement monitor page and see that the device updated only a few seconds ago, but the last temperature value it reports is from over a day ago and does not match the value shown on the device screen.

The cause

In the case of this Inkbird device, the temperature appears to be included in the keys of the manufacturer_data dict instead of being purely in the value.

This becomes a problem because the manufacturer_data dictionary doesn't appear to get cleared between readings and is just updated with new values. Because the temperature value is included in the key, a 'new' reading has the same value as the old reading, so the data is already in the dictionary and there's no change to the 'manufacturer_data'.

The value isn't the same for all temperature readings, but it appears to consistently be the same for the same temperature - I'm not sure what it actually contains but all that matters is that it's the same for this behaviour to occur.

This is the "Manufacturer Data" 'cache' from the Inkbird thermometer from the bluetooth/advertisement-monitor page (it's not sitting in the water right now, the water isn't getting that hot!):

Manufacturer data from an Inkbird thermometer {'1092': '000000d53f6408', '1102': '0000004d3e6408', '1112': '00000004fd6408', '1122': '000000dcf86408', '1132': '000000b5396408', '1142': '000000ecfb6408', '1152': '00000024ee6408', '1162': '000000bcef6408', '1172': '00000014ed6408', '1182': '0000008cec6408', '1192': '00000044e86408', '1202': '0000001d2a6408', '1212': '00000074eb6408', '1222': '000000ad216408', '1232': '000000e4e26408', '1242': '0000007ce36408', '1252': '00000055266408', '1262': '000000cd276408', '1272': '00000084e46408', '1282': '0000005d0c6408', '1292': '00000034cd6408', '1302': '0000006d0f6408', '1312': '000000a50b6408', '1322': '0000003d0a6408', '1332': '00000095086408', '1342': '0000000d096408', '1352': '000000c4c26408', '1362': '0000009d006408', '1372': '000000f4c16408', '1382': '0000002cc46408', '1392': '00000065076408', '1402': '000000fd066408', '1412': '000000d4d26408', '1422': '0000004cd36408', '1432': '00000005106408', '1442': '000000dd156408', '1452': '000000b4d46408', '1462': '000000ed166408', '1472': '00000024dd6408', '1482': '000000bcdc6408', '1492': '00000014de6408', '1502': '0000008cdf6408', '1512': '00000044db6408', '1522': '0000001d196408', '1532': '00000074d86408', '1542': '000000ac886408', '1552': '000000e54b6408', '1562': '0000007d4a6408', '1572': '000000548f6408', '1582': '000000cc8e6408', '1592': '000000854d6408', '1602': '0000005c876408', '1612': '00000035466408', '1622': '0000006c846408', '1632': '000000a4806408', '1642': '0000003c816408', '1652': '00000094836408', '1662': '0000000c826408', '1672': '000000c4976408', '1682': '0000009d556408', '1692': '000000f4946408', '1702': '0000002c916408', '1712': '00000065526408', '1722': '000000fd536408', '1732': '000000d5596408', '1742': '0000004d586408', '1752': '000000049b6408', '1762': '000000dc9e6408', '1772': '000000b55f6408', '1782': '000000ec9d6408', '1792': '00000025746408', '1802': '000000bd756408', '1812': '00000015776408', '1822': '0000008d766408', '1832': '00000045726408', '1842': '0000001cb06408', '1852': '00000075716408', '1862': '000000acbb6408', '1872': '000000e5786408', '1882': '0000007d796408', '1892': '00000054bc6408', '1902': '000000ccbd6408', '1912': '000000857e6408', '1922': '0000005d6a6408', '1932': '00000034ab6408', '1942': '0000006d696408', '1952': '000000a56d6408', '1962': '0000003d6c6408', '1972': '000000956e6408', '1982': '0000000d6f6408', '1992': '000000c4a46408', '2002': '0000009d666408', '2012': '000000f4a76408', '2022': '0000002ca26408', '2032': '00000065616408', '2042': '000000fd606408', '2052': '000000d7a06408', '2062': '0000004fa16408', '2072': '00000006626408', '2082': '000000de676408', '2092': '000000b7a66408', '2102': '000000ee646408', '2112': '00000027af6408', '2122': '000000bfae6408', '2132': '00000017ac6408', '2142': '0000008fad6408', '2152': '00000047a96408', '2162': '0000001e6b6408', '2172': '00000077aa6408', '2182': '000000afbe6408', '2192': '000000e67d6408', '2202': '0000007e7c6408', '2212': '00000057b96408', '2222': '000000cfb86408', '2232': '000000867b6408', '2242': '0000005fb16408', '2252': '00000036706408', '2262': '0000006fb26408', '2272': '000000a7b66408', '2282': '0000003fb76408', '2292': '00000097b56408', '2302': '0000000fb46408', '2312': '000000c65d6408', '2322': '0000009f9f6408', '2332': '000000f65e6408', '2342': '0000002e5b6408', '2352': '00000067986408', '2362': '000000ff996408', '2372': '000000d7936408', '2382': '0000004f926408', '2392': '00000006516408', '2402': '000000de546408', '2412': '000000b7956408', '2422': '000000ee576408', '2432': '00000026426408', '2442': '000000be436408', '2452': '00000016416408', '2462': '0000008e406408', '2472': '00000046446408', '2482': '0000001f866408', '2492': '00000076476408', '2502': '000000af8d6408', '2512': '000000e64e6408', '2522': '0000007e4f6408', '2532': '000000578a6408', '2542': '000000cf8b6408', '2552': '00000086486408', '2562': '0000005e186408', '2572': '00000037d96408', '2582': '0000006e1b6408', '2592': '000000a61f6408', '2602': '0000003e1e6408', '2612': '000000961c6408', '2622': '0000000e1d6408', '2632': '000000c7d66408', '2642': '0000009e146408', '2652': '000000f7d56408', '2662': '0000002fd06408', '2672': '00000066136408', '2682': '000000fe126408', '2692': '000000d7c66408', '2702': '0000004fc76408', '2712': '00000006046408', '2722': '000000de016408', '2732': '000000b7c06408', '2742': '000000ee026408', '2752': '00000027c96408', '2762': '000000bfc86408', '2772': '00000017ca6408', '2782': '0000008fcb6408', '2792': '00000047cf6408', '2802': '0000001e0d6408', '2812': '00000077cc6408', '2822': '000000ae246408', '2832': '000000e7e76408', '2842': '0000007fe66408', '2852': '00000056236408', '2862': '000000ce226408', '2872': '00000087e16408', '2882': '0000005e2b6408', '2892': '00000037ea6408', '2902': '0000006e286408', '2912': '000000a62c6408', '2922': '0000003e2d6408', '2932': '000000962f6408', '2942': '0000000e2e6408'}

This covers any temperature from 10.9 degrees to 29.4 degrees (which is the last reading shown in homeassistant) so the device will no longer report any temperature in that range. If a new packet comes in with {'2012': '000000f4a76408'} for example, the entry is already in the dict, so no change is detected and the entities don't update.

Running a Bleak scan locally on a linux machine shows similar behaviour with each new temperature reading adding a new entry unless it's the same value as before, where it appears there's no change to the manufacturer_data as that value is already in the dict. I can also see that the device is sending packets with the current temperature, but the entries match those already in the dict in homeassistant.

What version of Home Assistant Core has the issue?

2025.4.2

What was the last working version of Home Assistant Core?

No response

What type of installation are you running?

Home Assistant OS

Integration causing the issue

Inkbird

Link to integration documentation on our website

https://www.home-assistant.io/integrations/inkbird/

Diagnostics information

No response

Example YAML snippet


Anything in the logs that might be useful for us?


Additional information

I'm fairly certain this affects devices other than just this Inkbird device as any BLE device that produces non-standard(?) "Manufacturer Data" where the 'ID' is correlated with the data.

For example I've noticed with my ThermoPro TP357S devices that they can get out of date temperature readings if the humidity has been constant for a long time, but breathe on the device and change the humidity and suddenly the temperature will start to update again for a while until the humidity becomes more constant. I'm fairly sure this is the same issue and possibly a potential cause of this issue.

DavidCEllis avatar Apr 27 '25 15:04 DavidCEllis

Hey there @bdraco, mind taking a look at this issue as it has been labeled with an integration (inkbird) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of inkbird can trigger bot actions by commenting:

  • @home-assistant close Closes the issue.
  • @home-assistant rename Awesome new title Renames the issue.
  • @home-assistant reopen Reopen the issue.
  • @home-assistant unassign inkbird Removes the current integration label and assignees on the issue, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component) to the issue.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component) on the issue.

(message by CodeOwnersMention)


inkbird documentation inkbird source (message by IssueLinks)

home-assistant[bot] avatar Apr 27 '25 15:04 home-assistant[bot]

Unfortunately, this is a known limitation caused by how some vendors misuse the manufacturer data field in Bluetooth advertisements.

The BlueZ stack parses advertisement data before exposing it over D-Bus. When a device incorrectly fills the manufacturer field with raw data (without a proper manufacturer ID), BlueZ interprets each advertisement as coming from a different manufacturer. Because BlueZ merges parsed data before the Bleak stack receives it, we can't reliably tell which data is the newest — we can only compare the last known value to the current one and infer what has changed. If the data hasn't changed, we have no reliable way to know if a new advertisement has arrived.

While scanners like ESPHome and Shelly's can work around this, they are still designed to stay compatible with BlueZ so they remain interoperable with existing Linux-based implementations.

In short, truly fixing this would require replacing the entire BlueZ stack on Linux, which makes a full solution unlikely. We'll continue doing our best to handle these cases as gracefully as possible within the current constraints.

bdraco avatar Apr 27 '25 15:04 bdraco

In short though, this means this device literally can't work with the current setup and should probably be marked with some kind of warning on the integration page. The manufacturer_data will fill up and the device will just stop responding.

In some testing running bleak locally it seems running bluetoothctl remove <address> will at least clear the cache which removes all of the old entries, I don't know if there's any way of doing something equivalent through bleak or something else available to homeassistant - or if there's a way of preventing BlueZ from creating such a cache for some devices.

DavidCEllis avatar Apr 27 '25 18:04 DavidCEllis

It seems that vendors misusing the manufacturer_data field in this way will likely run into this issue. While we could add a general warning to the Bluetooth integration page about devices exhibiting this behavior, I’m not sure how helpful it would be, as users might have trouble identifying which devices are affected.

Cataloging every vendor that does this could result in a long list, and adding too many warnings to the documentation could make things a bit cluttered and harder to navigate.

On a positive note, the management API does provide raw data, though it requires root access to open the socket. For a longer-term solution, I’m thinking along these lines:

  1. New Method in bluetooth_data_tools:

    • Add a method to parse raw advertisement bytes directly, instead of relying on a group of tuples, to improve flexibility in handling raw data.
    • [x] https://github.com/Bluetooth-Devices/bluetooth-data-tools/pull/121
    • [x] https://github.com/home-assistant/core/pull/143782
    • [x] https://github.com/Bluetooth-Devices/habluetooth/pull/212
  2. New Method in habluetooth:

    • Add a method to accept raw advertising data, enabling easier handling of low-level Bluetooth data directly.
    • [x] https://github.com/Bluetooth-Devices/habluetooth/pull/213
  3. New raw Field in BluetoothServiceInfoBleak:

    • Introduce a raw field to store raw advertisement records, providing access to unprocessed data for further manipulation if needed.
    • [x] https://github.com/Bluetooth-Devices/habluetooth/pull/214
    • [x] https://github.com/home-assistant/core/pull/143787
  4. Side Channel for Bluetooth Management API (most of the work is here and in 5):

    • Implement a side channel in habluetooth to use the Bluetooth Management API (BlueZ), replacing the current Bleak-based advertising callbacks for better control over Bluetooth data and reducing dependency on Bleak.
    • This will require building a custom parser and implementing a C-based solution to handle the raw data efficiently. Since the Bluetooth data flow can be quite large (a firehose of information), the parser needs to be highly optimized to handle this volume without overloading the system.
    • There is no existing library to handle this, so a significant amount of work will go into creating an optimized, high-performance parser. This will take several months of careful optimization to ensure we don’t hit performance bottlenecks or overload the system.
    • https://github.com/Bluetooth-Devices/habluetooth/pull/219
  5. Suppress Existing Bleak Advertising Callbacks (most of the work is here and in 4):

    • If the Bluetooth Management API is available (requires root), suppress the existing Bleak advertising callbacks and use the raw data from the management API instead. If we can't access the raw data (e.g., due to lack of root privileges), we’ll gracefully fall back to using the existing Bleak callbacks.
  6. Integrate aioshelly with the New API:

    • Once the new raw API is implemented, update the aioshelly integration to utilize it.
    • [x] https://github.com/home-assistant-libs/aioshelly/pull/857
    • [x] https://github.com/home-assistant/core/pull/143814
  7. Integrate bleak-esphome with the New API:

    • Similarly, update the bleak-esphome library to leverage the new raw API.
    • [x] https://github.com/Bluetooth-Devices/bleak-esphome/pull/131
    • [x] https://github.com/home-assistant/core/pull/143787 ~~https://github.com/home-assistant/core/pull/143790~~
  8. Update the restore API to be able to restore the raw data:

    • Investigate how we can do this without a breaking change
    • move storage from bluetooth_adapters to habluetooth https://github.com/Bluetooth-Devices/habluetooth/pull/216
    • rewrite storage to be able to serialize and deserialize BluetoothServiceInfoBleak https://github.com/Bluetooth-Devices/habluetooth/pull/217
    • https://github.com/home-assistant/core/pull/143802
  9. Update libraries for devices that need the raw data:

    • [x] https://github.com/Bluetooth-Devices/inkbird-ble/pull/97 https://github.com/home-assistant/core/pull/143793
    • [x] https://github.com/Bluetooth-Devices/sensorpush-ble/pull/46 https://github.com/home-assistant/core/pull/143794
    • [x] https://github.com/Bluetooth-Devices/bluemaestro-ble/pull/32 https://github.com/home-assistant/core/pull/143795
    • [x] https://github.com/Bluetooth-Devices/sensorpro-ble/pull/30 https://github.com/home-assistant/core/pull/143796
    • [x] https://github.com/Bluetooth-Devices/thermobeacon-ble/pull/55 https://github.com/home-assistant/core/pull/143797
    • [x] https://github.com/Bluetooth-Devices/thermopro-ble/pull/89 https://github.com/home-assistant/core/pull/143799
    • [x] https://github.com/Bluetooth-Devices/leaone-ble/pull/35 https://github.com/home-assistant/core/pull/143798

Once the raw field is available, all relevant integrations will need to be updated to take advantage of it. If the raw data isn’t available (for example, on platforms like macOS where system APIs may not expose this data), the integration would need to gracefully fall back to the standard Bleak data. It’s important to note that macOS support might be challenging, as it doesn’t appear that any system APIs currently provide access to raw Bluetooth data.

Given the size and scope of this project, it’s likely to take some time—probably a few months (or years) to complete, since I can only work on it in my free time. But it’s definitely something I’d like to tackle!

bdraco avatar Apr 27 '25 18:04 bdraco

That does sound like a large project. I was thinking more if there was a shorter term solution where it would be possible to regularly clear the cache for devices that are known to misuse the data field or does that require access that HA won't have?

The thermopro devices showing the same behaviour are actually on a separate HA instance running HA core, so I can probably just run something in the background of that system that will clear their cache automatically which might make them more responsive - I don't currently have the same flexibility for the system with the inkbird device though.

DavidCEllis avatar Apr 27 '25 19:04 DavidCEllis

It’s important to note that macOS support might be challenging, as it doesn’t appear that any system APIs currently provide access to raw Bluetooth data.

Is MacOS known to have the same issue? I could try the scans using MacOS too if that would be useful?

DavidCEllis avatar Apr 27 '25 19:04 DavidCEllis

It’s important to note that macOS support might be challenging, as it doesn’t appear that any system APIs currently provide access to raw Bluetooth data.

Is MacOS known to have the same issue? I could try the scans using MacOS too if that would be useful?

I did some digging on that. It looks like we get the parsed data in separate callbacks. So it should be possible to write a replacement bleak driver which converts the data back to raw format. That alone is also another multi-month project.

2025-04-27 14:55:14.078 DEBUG (MainThread) [bleak.backends.corebluetooth.CentralManagerDelegate] Discovered device 0B7B412E-0E83-21A7-BD93-0EC325CA193A: GVH51275E3F @ RSSI: -60 (kCBAdvData <nsdict_keys(['kCBAdvDataIsConnectable', 'kCBAdvDataLocalName', 'kCBAdvDataManufacturerData', 'kCBAdvDataRxSecondaryPHY', 'kCBAdvDataTimestamp', 'kCBAdvDataRxPrimaryPHY'])>) and Central: <CBCentralManager: 0x600002894480>

bdraco avatar Apr 27 '25 19:04 bdraco

That does sound like a large project. I was thinking more if there was a shorter term solution where it would be possible to regularly clear the cache for devices that are known to misuse the data field or does that require access that HA won't have?

The thermopro devices showing the same behaviour are actually on a separate HA instance running HA core, so I can probably just run something in the background of that system that will clear their cache automatically which might make them more responsive - I don't currently have the same flexibility for the system with the inkbird device though.

Clearing the cache might not be the best approach, as it can cause issues with GATT connections and lead to sync problems. It could end up creating more challenges than it resolves.

bdraco avatar Apr 27 '25 19:04 bdraco

Clearing the cache might not be the best approach, as it can cause issues with GATT connections and lead to sync problems. It could end up creating more challenges than it resolves.

If it specifically removes the devices known to not give good data with bluetoothctl remove <address> can that affect other devices? Otherwise it would only potentially break devices that are already partially or completely broken. I wouldn't suggest doing it in general for well behaved devices.

I've setup a script that is currently running in the background of the system running HA core for the thermopro devices as an experiment anyway. I can disable it if it causes issues at least in that case.

Thanks for the quick responses though!

DavidCEllis avatar Apr 27 '25 20:04 DavidCEllis

If it specifically removes the devices known to not give good data with bluetoothctl remove <address> can that affect other devices? Otherwise it would only potentially break devices that are already partially or completely broken. I wouldn't suggest doing it in general for well behaved devices.

You probably won't run into issues in this limited case. However, I wouldn't recommend this as a general solution — it's not something I'd want to add to the library, given the potential for side effects and new problems once a GATT connection is needed. Also, the Inkbird library now falls back to GATT if active scans are disabled (in newer versions), so this approach would likely break that behavior. (Note: this fallback isn't part of a public release yet.)

bdraco avatar Apr 27 '25 20:04 bdraco

You probably won't run into issues in this limited case. However, I wouldn't recommend this as a general solution — it's not something I'd want to add to the library, given the potential for side effects and new problems once a GATT connection is needed.

It does feel pretty hacky to check if the dict is filling and then subprocess call out to bluetoothctl to remove the device just in order to clear the cache.

Also, the Inkbird library now falls back to GATT if active scans are disabled (in newer versions), so this approach would likely break that behavior. (Note: this fallback isn't part of a public release yet.)

Will the GATT fallback not suffer from the same issue, and is it easy to tell if that has occured? (Sorry, I'm not that knowledgable about bluetooth behaviour).

DavidCEllis avatar Apr 27 '25 21:04 DavidCEllis

You probably won't run into issues in this limited case. However, I wouldn't recommend this as a general solution — it's not something I'd want to add to the library, given the potential for side effects and new problems once a GATT connection is needed.

It does feel pretty hacky to check if the dict is filling and then subprocess call out to bluetoothctl to remove the device just in order to clear the cache.

Also, the Inkbird library now falls back to GATT if active scans are disabled (in newer versions), so this approach would likely break that behavior. (Note: this fallback isn't part of a public release yet.)

Will the GATT fallback not suffer from the same issue, and is it easy to tell if that has occured? (Sorry, I'm not that knowledgable about bluetooth behaviour).

GATT reads don't have the same limitations. But it won't use them if active scans are available.

bdraco avatar Apr 27 '25 21:04 bdraco

ESPHome and Shelly proxies will have the ability to expose the raw advertisement data needed to figure out the latest manufacturer data, even if a vendor misuses the field (which we're expecting with 2025.5.x).

Part 4/5 — handling local adapters — is the tricky part. It'll take a bit more time to design and implement since we need to work around some of the limitations in BlueZ and D-Bus.

As for macOS, we might not end up solving it there. The workaround would be almost as complex as what we need for BlueZ, and honestly, it’s probably not worth the extra effort compared to the impact.

bdraco avatar Apr 28 '25 08:04 bdraco

I've got a working proof of concept for local adapters. https://github.com/Bluetooth-Devices/habluetooth/pull/219

It still has a lot of issues to work though.. Maybe I'll be able to get it done this summer.

bdraco avatar May 03 '25 00:05 bdraco

Hi everyone, I'm a new user. I have the IBS-P01B bluetooth pool thermometer and it appears to be working (although only had for less than a day). I've been following this conversation but I'm not able to understand it. Is the above issue fixed, or will my thermometer soon stop reporting temperatures (once repeat temp values start coming in)?

mselkin avatar May 11 '25 10:05 mselkin

My understanding ( @bdraco may correct me if I'm wrong) is that in the current Home Assistant release it depends?

I believe 2025.5 has the fixes for devices that are connected to a bluetooth proxy such as ESPHome or Shelly devices, but not yet for active scanning with the bluetooth interface on the device running HA. Ours is connected to an ESP32 relay and appears to be working now.

It may also work if the integration uses the fallback to GATT.

Interestingly there are some other values that can change in the packets (no idea what these indicate) so it's not absolute that you don't get any updates on a local adapter connection it's just very spotty. These values didn't change when we had the device out of the pool[^1] because we initially thought it might be a signal issue and tried moving the device closer to the relay to check (which made things worse - we ended up with 2 days of constant temperature which we knew wasn't right).

Check for long stretches of constant temperature followed by large jumps in the graph.


Side note: Thanks to @bdraco for responding so quickly and working to resolve this.

[^1]: Hence it not being mentioned in the issue as it was out of the pool at that time.

DavidCEllis avatar May 11 '25 11:05 DavidCEllis

Thanks for your quick response. i do have ESP32Home bluetooth proxies for another project. Is there a way I could know if the thermometer is communicating via those rather than the bluetooth on the device running HomeAssistant? (I am using a HomeAssistant yellow).

mselkin avatar May 11 '25 12:05 mselkin

From the browser you can check homeassistant.local:8123/config/bluetooth/advertisement-monitor replacing homeassistant.local:8123 with your own device address. It should show which adapter is receiving messages from which device.

DavidCEllis avatar May 12 '25 17:05 DavidCEllis

I finished up the local adapter side of this. https://github.com/Bluetooth-Devices/habluetooth/pull/219

It was a lot more work than I expected so it took a few months.

I will start working on integrating it into Home Assistant soon.

bdraco avatar Aug 09 '25 07:08 bdraco