Support for Thermoplus / ThermoBeacon hydrometers
See original GitHub issueI built a BLE custom component for Thermoplus hydrometers a while ago, but having found this component which is much more feature rich, I think it makes sense to get the support in here instead of my limited implementation: https://github.com/araines/homeassistant-thermoplus-ble
In terms of how these work, the code above seems to mostly work, but here are some details:
The sensors appear to broadcast three different messages, one which identifies the sensor as a “ThermoBeacon”, one which contains the temp, humidity & battery data, and another one which I have not determined the purpose of it.
As an example, here is one of my sensors:
# hcitool lescan
LE Scan ...
9B:B0:00:00:02:60 (unknown)
9B:B0:00:00:02:60 ThermoBeacon
Then from the HCI dump:
# cat dump.txt | grep '60 02 00 00 B0 9B'
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 28 0B 64 01 9E 03 9A
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 30 0B 64 01 9E 03 9C
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 30 0B 64 01 9E 03 9D
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2D 0B 64 01 9E 03 9F
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2D 0B 64 01 9E 03 A0
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2D 0B 64 01 9E 03 A0
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2F 0B 64 01 9E 03 A2
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2F 0B 64 01 9E 03 A3
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2F 0B 64 01 9E 03 A5
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2F 0B 64 01 9E 03 A5
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 31 0B 64 01 9E 03 A9
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 31 0B 64 01 9E 03 AB
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2F 0B 64 01 9E 03 AB
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2F 0B 64 01 9E 03 AC
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 30 0B 64 01 9E 03 AE
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 30 0B 64 01 9E 03 AF
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
> 04 3E 29 02 01 00 00 60 02 00 00 B0 9B 1D 02 01 06 03 02 F0
FF 15 FF 11 00 00 00 60 02 00 00 B0 9B 2F 0B 64 01 9E 03 B2
> 04 3E 2B 02 01 00 00 60 02 00 00 B0 9B 1F 02 01 06 03 02 F0
FF 17 FF 11 00 00 00 60 02 00 00 B0 9B 95 01 1B 45 0D 00 11
Approximately at the time of the above dump the sensor read close to: 22.3C 57% humidity.
This test file shows basically how the decoding works: https://github.com/araines/homeassistant-thermoplus-ble/blob/master/test.py Giving this output:
RAW1 (Med packet - 44 bytes)
('06:88:00:00:16:27',)
[{'data_type': b'\x01', 'length': 2, 'value': b'\x06'}, {'data_type': b'\x02', 'length': 3, 'value': b'\xf0\xff'}, {'data_type': b'\xff', 'length': 21, 'value': b"\x11\x00\x00\x00'\x16\x00\x00\x88\x06<\x0c\x8f\x01\xa1\x03\xb9\xd7\x03"}]
mac: 06:88:00:00:16:27
Payload: 3132 24.94 58.06 -56
RAW2 (Long packet - 46 bytes)
('06:88:00:00:16:27',)
[{'data_type': b'\x01', 'length': 2, 'value': b'\x06'}, {'data_type': b'\x02', 'length': 3, 'value': b'\xf0\xff'}, {'data_type': b'\xff', 'length': 23, 'value': b"\x11\x00\x00\x00'\x16\x00\x00\x88\x06\xfa\x01\x98\x17\x00\x00T\x01Ns\x02"}]
RAW3 (Short packet - 38 bytes)
('06:88:00:00:16:27',)
[{'data_type': b'\t', 'length': 13, 'value': b'ThermoBeacon'}, {'data_type': b'\x12', 'length': 5, 'value': b'\x18\x008\x01'}, {'data_type': b'\n', 'length': 2, 'value': b''}]
ThermoBeacon
Noting here that the medium length message has the payload:
3132 - Battery voltage in mV
24.94 - Temperature in C
58.06 - Humidity %
-56 - Signal strength (RSSI)
According to the reverse-engineered app code, the battery voltage is interpreted as such:
Voltage | Level |
---|---|
>=3000 | 100% |
>=2800 | 80% |
>=2600 | 60% |
>=2500 | 40% |
>=2450 | 20% |
<2450 | 0% |
The temperature and humidity values are the raw value divided by 16 to get the true values (details in the code).
For auto-discovery my implementation looks for sensors emitting the ThermoBeacon identifier, then remembers those MAC addresses. Any subsequent messages emitted from those MAC addresses are analysed to find the correct (medium length) message and then decoded to get the associated data.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:37
Same - all looking good to me now! Thanks for everything 😃
Yup that did the trick. Thanks!