Usage
inkbird-ble turns the BLE advertisements (and, for some models, GATT
notifications/reads) emitted by INKBIRD devices into structured sensor values.
It is built on top of
bluetooth-sensor-state-data
and is the parser used by the Home Assistant INKBIRD integration, but it can be
used on its own.
The entry point is the INKBIRDBluetoothDeviceData class:
from inkbird_ble import INKBIRDBluetoothDeviceData
Passive devices (advertisement only)
Most INKBIRD sensors (the IBS-TH, IBS-TH2, ITH-*-B, iBBQ-* and IAM-T2
families) broadcast their readings in the BLE advertisement, so no connection is
required. Feed each advertisement to update() and you get a SensorUpdate
back:
from inkbird_ble import INKBIRDBluetoothDeviceData
# `service_info` is a habluetooth `BluetoothServiceInfoBleak`. Inside Home
# Assistant you receive one for every advertisement; standalone you can build
# one from a Bleak scan (see below).
data = INKBIRDBluetoothDeviceData()
if data.supported(service_info):
update = data.update(service_info)
print(data.device_type) # e.g. Model.IBS_TH
print(update.entity_values)
update() returns a SensorUpdate whose entity_values maps each
DeviceKey to a SensorValue (temperature in °C, humidity in %, battery in %,
signal strength in dBm, …):
for device_key, sensor_value in update.entity_values.items():
print(device_key.key, sensor_value.native_value)
# temperature 20.44
# humidity 48.07
# battery 86
# signal_strength -60
What is the integer key in manufacturer_data?
A BluetoothServiceInfoBleak exposes manufacturer_data as a
dict[int, bytes]. The integer key is the Bluetooth SIG company identifier
advertised by the device — it is not a temperature or a value you need to
decode yourself. You never index into manufacturer_data manually; pass the
whole service_info to update() and the parser selects the right model and
payload for you.
If your device firmware also exposes the raw advertisement bytes, populate the
raw field of the BluetoothServiceInfoBleak — when present, the parser
prefers it over manufacturer_data, which avoids a class of misreads.
Active devices (connect, read or subscribe)
A few models do not put everything in the advertisement and must be polled or
subscribed to over a GATT connection. inkbird-ble handles the connection for
you via Bleak; you only supply a BLEDevice.
If you know the model up front, pass it to the constructor (otherwise it is detected from the first advertisement):
from inkbird_ble import INKBIRDBluetoothDeviceData
data = INKBIRDBluetoothDeviceData("IBS-TH")
data.supported(service_info) # also sets the device type
Polling models
For models that expose their data through a readable characteristic, check
poll_needed() and call async_poll() with a BLEDevice:
if data.poll_needed(service_info, last_poll=None):
update = await data.async_poll(ble_device)
print(update.entity_values)
poll_needed() rate-limits itself, so it is safe to call on every
advertisement; it only returns True when a fresh read is actually due.
Notify models
Some models (for example the IAM-T1 and the IHT-2PB probe thermometer) push
readings over GATT notifications. Start a notification session and receive
updates through callbacks:
def on_update(update):
print(update.entity_values)
def on_device_data_changed(device_data):
# e.g. the device's temperature unit changed
print(device_data)
data = INKBIRDBluetoothDeviceData(
"IAM-T1",
update_callback=on_update,
device_data_changed_callback=on_device_data_changed,
)
await data.async_start(service_info, ble_device)
# ... receive callbacks while connected ...
await data.async_stop()
Use the uses_notify property to tell the two active styles apart:
if data.uses_notify:
await data.async_start(service_info, ble_device)
elif data.poll_needed(service_info, last_poll=None):
await data.async_poll(ble_device)
Building a BluetoothServiceInfoBleak outside Home Assistant
When you are not running inside Home Assistant you can construct the
service_info yourself from a Bleak scan result:
from habluetooth import BluetoothServiceInfoBleak
service_info = BluetoothServiceInfoBleak(
name=device.name,
address=device.address,
rssi=advertisement_data.rssi,
manufacturer_data=advertisement_data.manufacturer_data,
service_data=advertisement_data.service_data,
service_uuids=advertisement_data.service_uuids,
source="local",
device=device,
advertisement=advertisement_data,
connectable=True,
time=0,
tx_power=advertisement_data.tx_power or 0,
raw=None,
)
device and advertisement_data are the BLEDevice and AdvertisementData
objects yielded by bleak.BleakScanner.