BTHome logo

BLE advertising in the BTHome v2 format

BTHome v2 was released in November 2022. Click here for v1 docs.

The BTHome v2 format can best be explained with an example. First, the basics:
Bluetooth defines a single packet format for both advertising and data transmissions. This packet consist of four components: preamble (1 octet), access address (4 octets), Protocol Data Unit - PDU (2-257 octets), and Cyclic Redundancy Check - CRC (3 octets). If you want to learn more about the BLE format, you can read this.

BLE packet

Advertising payload

The actual data is being sent as part of the PDU, as the Payload, as indicated in green. The advertising payload is a long message with bytes (bytestring). An example of a BTHome payload is: 020106 0B094449592D73656E736F72 0A16D2FC4002C40903BF13

The advertising payload can consist of one or more Advertising Data (AD) elements. Each element contains the following:

The advertising payload in BTHome format should at least contain the AD element Service Data (16 bits UUID). It is also strongly advised to include the AD element Flags. Optionally, you can add a Local Name.

Flags (0x01)

The flag value has several bits indicating the capabilities of the device. Although not required, it is still strongly advised to include this element in your advertisement, especially when using passive scanning on the receiving side. Bluez, which e.g. is used in the Home Assistant Bluetooth integration, won't be able to parse the data without this flags value. This part is always the same for BTHome (0x020106).

Flags data: 020106 Service data (16-bit UUID) (0x16)

The service data contains the actual data. In the example, we have:

Local Name (0x08 or 0x09)

Optional AD element is the local name, either the Shortened local name (0x08) or Complete local name (0x09).

BTHome Data format

The BTHome (service) data contains the 16 bit UUID, the BTHome Device Information and one or multiple Measurements.

In the example we have the following BTHome data:
0xD2FC 40 02C409 03BF13

UUID (D2FC)

The UUID has to be read reversed per byte, so the UUID is 0xFCD2. This UUID should be used by receivers to recognize BTHome messages.

Allterco Robotics, the manufacturer of Shelly devices, has sponsored this UUID for BTHome and it's free to use for everyone with this license.

BTHome Device Information (0x40)

The first byte after the UUID 0x40 is the BTHome device info byte, which has several bits indicating the capabilities of the device.

In the example, we have 0x40. After converting this to bits, we get 01000000.
Note. bit 0 is the most right number, bit 7 is the most left number

Temperature measurement (0x02 C409)

Humidity measurement (0x03 BF13)

Object ids have to be applied in numerical order (from low to high) in your advertisement. This will make sure that if you have a device (sensor) that is broadcasting a new measurement type that is added in a new (minor) BTHome update, while your BTHome receiver isn't updated yet to the same version, it will still be able to receive the older supported measurement types. A BTHome receiver will stop parsing object ids as soon as it finds an object id that isn't supported.

Supported data types

Full example payloads for each data type.

Sensor data

Object id Property Data type Factor Example Result Unit
0x51 acceleration uint16 (2 bytes) 0.001 518756 22.151 m/s²
0x01 battery uint8 (1 byte) 1 0161 97 %
0x12 co2 uint16 (2 bytes) 1 12E204 1250 ppm
0x09 count uint (1 bytes) 1 0960 96
0x3D count uint (2 bytes) 1 3D0960 24585
0x3E count uint (4 bytes) 1 3E2A2C0960 1611213866
0x43 current uint16 (2 bytes) 0.001 434E34 13.39 A
0x08 dewpoint sint16 (2 bytes) 0.01 08CA06 17.38 °C
0x40 distance (mm) uint16 (2 bytes) 1 400C00 12 mm
0x41 distance (m) uint16 (2 bytes) 0.1 414E00 7.8 m
0x42 duration uint24 (3 bytes) 0.001 424E3400 13.390 s
0x4D energy uint32 (4 bytes) 0.001 4d12138a14 344593.170 kWh
0x0A energy uint24 (3 bytes) 0.001 0A138A14 1346.067 kWh
0x4B gas uint24 (3 bytes) 0.001 4B138A14 1346.067 m3
0x4C gas uint32 (4 bytes) 0.001 4C41018A01 25821.505 m3
0x52 gyroscope uint16 (2 bytes) 0.001 528756 22.151 °/s
0x03 humidity uint16 (2 bytes) 0.01 03BF13 50.55 %
0x2E humidity uint8 (1 byte) 1 2E23 35 %
0x05 illuminance uint24 (3 bytes) 0.01 05138A14 13460.67 lux
0x06 mass (kg) uint16 (2 byte) 0.01 065E1F 80.3 kg
0x07 mass (lb) uint16 (2 byte) 0.01 073E1D 74.86 lb
0x14 moisture uint16 (2 bytes) 0.01 14020C 30.74 %
0x2F moisture uint8 (1 byte) 1 2F23 35 %
0x0D pm2.5 uint16 (2 bytes) 1 0D120C 3090 ug/m3
0x0E pm10 uint16 (2 bytes) 1 0E021C 7170 ug/m3
0x0B power uint24 (3 bytes) 0.01 0B021B00 69.14 W
0x04 pressure uint24 (3 bytes) 0.01 04138A01 1008.83 hPa
0x54 raw see below - 540C48656C6C6F 20576F726C6421 48656c6c6f20 576f726c6421
0x3F rotation sint16 (2 bytes) 0.1 3F020C 307.4 °
0x44 speed uint16 (2 bytes) 0.01 444E34 133.90 m/s
0x45 temperature sint16 (2 bytes) 0.1 451101 27.3 °C
0x02 temperature sint16 (2 bytes) 0.01 02CA09 25.06 °C
0x53 text see below - 530C48656C6C6F 20576F726C6421 Hello World!
0x50 timestamp uint48 (4 bytes) - 505d396164 see below
0x13 tvoc uint16 (2 bytes) 1 133301 307 ug/m3
0x0C voltage uint16 (2 bytes) 0.001 0C020C 3.074 V
0x4A voltage uint16 (2 bytes) 0.1 4A020C 307.4 V
0x4E volume uint32 (4 bytes) 0.001 4E87562A01 19551.879 L
0x47 volume uint16 (2 bytes) 0.1 478756 2215.1 L
0x48 volume uint16 (2 bytes) 1 48DC87 34780 mL
0x55 volume storage uint32 (4 bytes) 0.001 5587562A01 19551.879 L
0x49 volume Flow Rate uint16 (2 bytes) 0.001 49DC87 34.780 m3/hr
0x46 UV index uint8 (1 byte) 0.1 4632 5.0
0x4F water uint32 (4 bytes) 0.001 4F87562A01 19551.879 L
Timestamp

The timestamp sensor needs a little more explanation. The timestamp sensor is defined as number of seconds since 1970-1-1, 00:00:00, UTC, (also called epoch time), and is returning a datetime object with timezone UTC. 0x5D396164 is, after converting from bytes (little endian) to an integer, 1684093277 seconds, which corresponds to 2023-5-14, 19:41:17. The corresponding datetime object that is returned is: datetime(2023, 5, 14, 19, 41, 17, tzinfo=timezone.utc)

Text and Raw sensors

The text and raw sensor have a variable length. You therefore have to specify the length in the first byte after the `object id`. In the example of the text sensor 0x530C48656C6C6F20576F726C6421, the 2nd byte (0x0C) gives the length of the text string (12 bytes). Text has to be encoded in UTF-8, while the raw sensor is reported back as hexadecimal string. The byte string 0x48656C6C6F20576F726C6421 in text is Hello World!, while in raw sensor will give 48656c6c6f20576f726c6421 as result. Make sure the length is specified correct in the second byte.

Multiple measurements of the same type

If you want to send multiple measurements of the same type, e.g. three temperatures, you can just add multiple measurements of the same type to the payload. A postfix will be added to the measurement name (e.g. temperature_2) in the order of which you define the measurements. Note that this implies that you will need to use the same order in each advertisement, to prevent measurements being assigned to the wrong entity. If only one measurement of a certain type is sent, no postfix will be used.

Binary Sensor data

Binary sensor data should always be an uint8 of a single byte. Its value should be 1 for on, and 0 for off.

Object id Property Data type Example Result
0x15 battery uint8 (1 byte) 1501 0 (False = Normal)
1 (True = Low)
0x16 battery charging uint8 (1 byte) 1601 0 (False = Not Charging)
1 (True = Charging)
0x17 carbon monoxide uint8 (1 byte) 1700 0 (False = Not detected)
1 (True = Detected)
0x18 cold uint8 (1 byte) 1801 0 (False = Normal)
1 (True = Cold)
0x19 connectivity uint8 (1 byte) 1900 0 (False = Disconnected)
1 (True = Connected)
0x1A door uint8 (1 byte) 1A00 0 (False = Closed)
1 (True = Open)
0x1B garage door uint8 (1 byte) 1B01 0 (False = Closed)
1 (True = Open)
0x1C gas uint8 (1 byte) 1C01 0 (False = Clear)
1 (True = Detected)
0x0F generic boolean uint8 (1 byte) 0F01 0 (False = Off)
1 (True = On)
0x1D heat uint8 (1 byte) 1D00 0 (False = Normal)
1 (True = Hot)
0x1E light uint8 (1 byte) 1E01 0 (False = No light)
1 (True = Light detected)
0x1F lock uint8 (1 byte) 1F01 0 (False = Locked)
1 (True = Unlocked)
0x20 moisture uint8 (1 byte) 2001 0 (False = Dry)
1 (True = Wet)
0x21 motion uint8 (1 byte) 2100 0 (False = Clear)
1 (True = Detected)
0x22 moving uint8 (1 byte) 2201 0 (False = Not moving)
1 (True = Moving)
0x23 occupancy uint8 (1 byte) 2301 0 (False = Clear)
1 (True = Detected)
0x11 opening uint8 (1 byte) 1100 0 (False = Closed)
1 (True = Open)
0x24 plug uint8 (1 byte) 2400 0 (False = Unplugged)
1 (True = Plugged in)
0x10 power uint8 (1 byte) 1001 0 (False = Off)
1 (True = On)
0x25 presence uint8 (1 byte) 2500 0 (False = Away)
1 (True = Home)
0x26 problem uint8 (1 byte) 2601 0 (False = OK)
1 (True = Problem)
0x27 running uint8 (1 byte) 2701 0 (False = Not Running)
1 (True = Running)
0x28 safety uint8 (1 byte) 2800 0 (False = Unsafe)
1 (True = Safe)
0x29 smoke uint8 (1 byte) 2901 0 (False = Clear)
1 (True = Detected)
0x2A sound uint8 (1 byte) 2A00 0 (False = Clear)
1 (True = Detected)
0x2B tamper uint8 (1 byte) 2B00 0 (False = Off)
1 (True = On)
0x2C vibration uint8 (1 byte) 2C01 0 (False = Clear)
1 (True = Detected)
0x2D window uint8 (1 byte) 2D01 0 (False = Closed)
1 (True = Open)

Events

Devices can also send events. Events are a combination of a device type (like a button or dimmer) and an event (like "press" or "long press" or "rotate left 3 steps"). Note: Events will be supported in Home Assistant 2023.5 and higher.

Object id Device type Event id Event type Event property Example Result
0x3A button 0x00 None 3A00
0x01 press 3A01 press
0x02 double_press 3A02 double_press
0x03 triple_press 3A03 triple_press
0x04 long_press 3A04 long_press
0x05 long_double_press 3A05 long_double_press
0x06 long_triple_press 3A06 long_triple_press
0x3C dimmer 0x00 None 3C0000
0x01 rotate left # steps 3C0103 rotate left 3 steps
0x02 rotate right # steps 3C020A rotate right 10 steps
Multiple events of the same type

Similar as for sensors and binary sensors, you can also send multiple events of the same type, by just adding them behind each other. The event `0x00` (None) is a special event that can be used in case you e.g. have a device with two buttons, and you only want to send an event for the 2nd button. An event 3A003A01 will send no event for the first button, and an event "press" for the second button.

Device

Device information

The following device information can be included in the BLE advertisment, a device type id and the installed fw version. The device type id is 2 bytes long in little endian and can be used to make a distinction between device types. The firmware version can be send in 0.0.0.1 format or in 0.0.1 format.

Object id Property Data Type Example Result
0xF0 device type id uint16 (2 bytes) F00100 1
0xF1 firmware version uint32 (4 bytes) F100010204 4.2.1.0
0xF2 firmware version uint24 (3 bytes) F1000106 6.1.0

Misc data

Packet id

The packet id is optional and can be used to filter duplicate data. This allows you to send multiple advertisements that are exactly the same, to improve the chance that the advertisement arrives. BTHome receivers should only process the advertisement if the packet id is different compared to the previous one. The packet id is a value between 0 (0x00) and 255 (0xFF), and should be increased on every change in data. Note that most home automation software already have some other filtering for unchanged data. The use of a packet id is therefore often not needed.

Object id Property Data Type Example Result
0x00 packet id uint8 (1 byte) 0009 9

Encryption

Unencrypted BLE advertisements can be read by anyone nearby listening for Bluetooth packets. BTHome supports AES encryption (CCM mode) which works with a pre-shared key. When encrypted, the data can only be read by knowing the encryption key. The encryption key should be a 16 bytes long key (32 characters).

More detailed information about encryption in BTHome is given on this page.