If it helps, this is all of the values, I have not been able to successfully control my unit
FWIW, I was expecting the packet capture to start with some form of handshake, which stops the airwasher from disconnecting after a timeout.
Also I’m not sure what the values you shared mean. I hoped for a BLE service/characteristic UUIDs and a value, like here: https://youtu.be/_2RuJ5-pF5I
These are the values I sent from my phone to boneco, I connected my old android phone and I’m sniffing it.
i can share service uuid and uuid, but I expect they will be different always for different devices
Ther3 is some kind of a handshace, there is 3 writes without a value, I guess it is handshake, they have different uuid client charasteristics that are send, but I cant understand how to send them properly. In your tutorial there is also no information about handshake, how to do it.
What information would you need more to understand?
That values that I shared are so long sent to boneco and received from it, they hold some kind of constants and value inside and dynamic value by time as I understand
i can share service uuid and uuid, but I expect they will be different always for different devices
Nope, they are vendor-specific, but should not differ across devices of the same model. My unit has Service UUID fdce123410134120b9191dbb32a2d132 / Characteristic UUID fdce234610134120b9191dbb32a2d132, just like yours.
But: after reading up on BLE, it seems you just need to know the handle ID (which determines the service/characteristic/descriptor accessed) and the value written. Your doc only has the values, right?
I cant understand how to send them properly. In your tutorial there is also no information about handshake, how to do it.
Yep, every device is different, and it’s probably impossible to use the nRF Connect app here because of the quick disconnect
Still, the idea is to first determine if the BLE communication looks easily reproducible, by performing the same simple sequence (e.g. connect - turn off - disconnect) repeatedly, and checking if it results in identical ATT requests (with the same handles/values).
If we’re lucky, the next step would be to try and reproduce the requests using a client you control. I think the highlighted row from your screenshot can be imitated like this:
gatttool -b xx:xx:xx:xx:xx:xx -t random -I
connect
char-write-req 0x0021 0x01
…but I’m not sure about the best tool to do so: gatttool does not seem very well suitable for scripting and is being deprecated. Home Assistant seems to use bleak, if one is ready to write some Python.
Hi guys. I have three BONECO H700 and also interested in integrating them into Home Assistant. Will this method work for BONECO?
Reverse Engineering Unsupported BT Devices and Integrating into HA
Hi there! I have 2 devices H700 as well.Also interested in the integrations. I have an Android phone, tried to capture logs, but there are tons of noise in it.
Ahh, that sucks
guys, no updates?
Hmm, I’m just stuck, because I do not know how to communicate with the device properly, I was not able to connect and send the command
Android app does these steps every start:
- connect to device
- send auth request (using device key from pairing) and wait response (starts with bytes 040102)
- read device name (just c-string)
- read device state - byte array (little-endian encoding)
- 0 - fan / fan+fan_mode
- 1 - timer+history_active+lock+on_off_state
- 2 - target humidity / nothing
- 3 - change filter + change iss + clean + change water
- 4-7 - filter data (counter or date)
- 8-11 - iss data (counter or date)
- 12-15 - clean data (counter or date)
- 16 - on_off_timer_hours + clean mode support + on_off_timer_status
- 17 - on_off_timer_minutes
- 18 - min led brightness
- 19 - max led brightness
some bytes are only for fan devices, some for P series. I’ve tested with W400 around 2 years ago
Did anyone figure this out ?
I think I have made progress but not sure
I’m stuck on trying to bypass the handshake and stop it from disconnecting. Any suggestions?
Start with bleakclient.start_notify(CHARACTERISTIC_AUTH, callback)
and def callback(sender: int, data: bytearray) -> None
.
Your callback should check that sender == 0x026
and
- Save “nonce” if
len(data) == 20 and data[0] == 1
- Save “auth level/step” from
data[1]
ifdata[0] == 4 and data[2] == 2
- Save “device key” from
data[3:19]
ifdata[0:3] == b'\x06\x00\x00'
After getting “nonce” you should also call bleakclient.start_notify(CHARACTERISTIC_AUTH_AND_SERVICE, callback2)
and start calling bleakclient.write_gatt_char(CHARACTERISTIC_AUTH_AND_SERVICE, some_data)
for moving between auth states
callback2 just checks that sender == 0x029 and data[1] & 1 == 1
CHARACTERISTIC_AUTH = "fdce2347-1013-4120-b919-1dbb32a2d132"
CHARACTERISTIC_AUTH_AND_SERVICE = "fdce2348-1013-4120-b919-1dbb32a2d132"
Auth states: GOT_NONCE → CONFIRM_WAITING (here you press button on device) → CONFIRMED → GOT_DEVICE_KEY → AUTH_SUCCESS
im just getting this error
Started notifications for AUTH characteristic
callback invoked with sender: fdce2347-1013-4120-b919-1dbb32a2d132 (Handle: 38): Unknown, data: 0101c88b5474b03218a41249a5d396afcca20000
callback invoked with sender: fdce2347-1013-4120-b919-1dbb32a2d132 (Handle: 38): Unknown, data: 0400010000000000000000000000000000000000
with this script
import asyncio
from bleak import BleakClient
CHARACTERISTIC_AUTH = "fdce2347-1013-4120-b919-1dbb32a2d132"
CHARACTERISTIC_AUTH_AND_SERVICE = "fdce2348-1013-4120-b919-1dbb32a2d132"
DEVICE_MAC_ADDRESS = "E1:A6:44:55:39:CA"
# Global variables to store the data and state
nonce = None
auth_level_step = None
device_key = None
auth_state = "INIT"
def callback(sender: str, data: bytearray) -> None:
global nonce, auth_level_step, device_key, auth_state
print(f"callback invoked with sender: {sender}, data: {data.hex()}")
if sender == CHARACTERISTIC_AUTH:
if len(data) == 20 and data[0] == 1:
nonce = data
auth_state = "GOT_NONCE"
print("Nonce saved:", nonce.hex())
asyncio.create_task(start_notify_auth_and_service())
elif data[0] == 4 and data[2] == 2:
auth_level_step = data[1]
auth_state = "CONFIRM_WAITING"
print("Auth level/step saved:", auth_level_step)
elif data[0:3] == b'\x06\x00\x00':
device_key = data[3:19]
auth_state = "GOT_DEVICE_KEY"
print("Device key saved:", device_key.hex())
auth_state = "AUTH_SUCCESS"
print("Authentication successful")
def callback2(sender: str, data: bytearray) -> None:
print(f"callback2 invoked with sender: {sender}, data: {data.hex()}")
if sender == CHARACTERISTIC_AUTH_AND_SERVICE and (data[1] & 1) == 1:
print("Callback2 triggered with valid data.")
# Here you can implement the logic to move between authentication states
async def start_notify_auth_and_service():
print("Connecting to start AUTH_AND_SERVICE notifications...")
async with BleakClient(DEVICE_MAC_ADDRESS) as client:
await client.start_notify(CHARACTERISTIC_AUTH_AND_SERVICE, callback2)
some_data = bytearray([0x01, 0x02, 0x03]) # Replace this with actual data as needed
await asyncio.sleep(1) # Allow time for notifications to stabilize
await client.write_gatt_char(CHARACTERISTIC_AUTH_AND_SERVICE, some_data)
print("Started notifications for AUTH_AND_SERVICE characteristic")
async def main():
print(f"Connecting to device {DEVICE_MAC_ADDRESS}...")
async with BleakClient(DEVICE_MAC_ADDRESS) as client:
await client.start_notify(CHARACTERISTIC_AUTH, callback)
print("Started notifications for AUTH characteristic")
while auth_state != "AUTH_SUCCESS":
await asyncio.sleep(1)
if auth_state == "GOT_NONCE":
print("Press the button on the device to confirm...")
if __name__ == "__main__":
asyncio.run(main())
Can you please share your code? I am new to bluetooth communication to be true, but want to try it.
I think you have a mistake in sender == CHARACTERISTIC_AUTH
you should use something like str(sender).split(' ')[0] == CHARACTERISTIC_AUTH
I updated the script but still couldn’t get it to work
import asyncio
from bleak import BleakClient
from bleak.exc import BleakError, BleakDBusError
CHARACTERISTIC_AUTH = "fdce2347-1013-4120-b919-1dbb32a2d132"
CHARACTERISTIC_AUTH_AND_SERVICE = "fdce2348-1013-4120-b919-1dbb32a2d132"
DEVICE_MAC_ADDRESS = "E1:A6:44:55:39:CA"
# Global variables to store the data and state
nonce = None
auth_level_step = None
device_key = None
auth_state = "INIT"
def callback(sender: str, data: bytearray) -> None:
global nonce, auth_level_step, device_key, auth_state
sender_str = str(sender).split(' ')[0]
print(f"callback invoked with sender: {sender_str}, data: {data.hex()}")
if sender_str == CHARACTERISTIC_AUTH:
if len(data) == 20 and data[0] == 1:
nonce = data
auth_state = "GOT_NONCE"
print("Nonce saved:", nonce.hex())
asyncio.create_task(start_notify_auth_and_service())
elif data[0] == 4 and data[2] == 2:
auth_level_step = data[1]
auth_state = "CONFIRM_WAITING"
print("Auth level/step saved:", auth_level_step)
elif data[0:3] == b'\x06\x00\x00':
device_key = data[3:19]
auth_state = "GOT_DEVICE_KEY"
print("Device key saved:", device_key.hex())
auth_state = "AUTH_SUCCESS"
print("Authentication successful")
def callback2(sender: str, data: bytearray) -> None:
sender_str = str(sender).split(' ')[0]
print(f"callback2 invoked with sender: {sender_str}, data: {data.hex()}")
if sender_str == CHARACTERISTIC_AUTH_AND_SERVICE and (data[1] & 1) == 1:
print("Callback2 triggered with valid data.")
# Implement the logic to move between authentication states
async def start_notify_auth_and_service():
retry_attempts = 3
for attempt in range(retry_attempts):
try:
print("Connecting to start AUTH_AND_SERVICE notifications...")
async with BleakClient(DEVICE_MAC_ADDRESS) as client:
await client.start_notify(CHARACTERISTIC_AUTH_AND_SERVICE, callback2)
some_data = bytearray([0x01, 0x02, 0x03]) # Replace this with actual data as needed
await asyncio.sleep(1) # Allow time for notifications to stabilize
await client.write_gatt_char(CHARACTERISTIC_AUTH_AND_SERVICE, some_data)
print("Started notifications for AUTH_AND_SERVICE characteristic")
break
except BleakDBusError as e:
print(f"Failed to start notifications (attempt {attempt + 1}): {e}")
await asyncio.sleep(2)
except BleakError as e:
print(f"BLE error (attempt {attempt + 1}): {e}")
await asyncio.sleep(2)
async def main():
print(f"Connecting to device {DEVICE_MAC_ADDRESS}...")
try:
async with BleakClient(DEVICE_MAC_ADDRESS) as client:
await client.start_notify(CHARACTERISTIC_AUTH, callback)
print("Started notifications for AUTH characteristic")
while auth_state != "AUTH_SUCCESS":
await asyncio.sleep(1)
if auth_state == "GOT_NONCE":
print("Press the button on the device to confirm...")
except BleakDBusError as e:
print(f"Failed to connect to device: {e}")
except BleakError as e:
print(f"BLE error: {e}")
if __name__ == "__main__":
asyncio.run(main())
Any news on that? have two h700 as well