tuya-ble-sdk
Python SDK for Tuya Bluetooth Low Energy devices. It speaks the Tuya BLE GATT protocol directly — handshake, session key, encrypted frames and datapoints — and knows nothing about Home Assistant. It also reads the device credentials off the Tuya account, since the handshake needs values the device never broadcasts.
Consumed by the ha-tuya-ble
integration, which pins it from manifest.json.
What it does
One read is one whole session: the client connects, performs the handshake, collects the datapoint report and disconnects. Tuya BLE sensors are battery powered and only listen for a moment after they advertise, so holding a connection open would drain them and occupy a proxy slot for nothing.
from tuya_ble_sdk import TuyaBleClient, TuyaBleCredentials, parse_advertisement
info = parse_advertisement(service_data, manufacturer_data)
client = TuyaBleClient(
ble_device,
TuyaBleCredentials(uuid=info.uuid, device_id=device_id, local_key=local_key),
)
data_points = await client.async_read_data_points()
Discovery belongs to the caller: the client takes an already-resolved
BLEDevice, which is what lets Home Assistant hand over a device seen through
a Bluetooth proxy.
parse_advertisement reads what the advertisement discloses — every field of
the result is optional. The uuid is encrypted with the product-id record
broadcast beside it, so no cloud call is needed to learn it; the readable
product id, however, is only there on an unbound device. One bound to a
Tuya account broadcasts an obfuscated value in its place: those bytes still
decrypt the uuid, but they name no product, and the caller has to learn what
the device is some other way.
Credentials from the account
A session needs a device id and a local key, and no device discloses either:
only the Tuya account that owns it does. TuyaBleCloudClient logs into the
mobile app gateway with the account's e-mail and password and returns what it
knows about every device on it.
from tuya_ble_sdk import TuyaBleCloudClient
async with TuyaBleCloudClient(email, password, country_code, region="us") as cloud:
devices = await cloud.async_list_devices()
paired = next(device for device in devices if device.uuid == info.uuid)
credentials = TuyaBleCredentials(
uuid=paired.uuid, device_id=paired.device_id, local_key=paired.local_key
)
The account describes a Bluetooth device with the same uuid its
advertisement carries and with its mac, so either one ties an account record
to a device seen over the air. The record also names the product_id a bound
device stops broadcasting.
Region is the account's data centre — one of us, eu, cn, in, we — and
country_code is the calling code the account was registered with (55 for
Brazil). Nothing is cached: one client is one login.
Command line
The optional cli extra installs a tuya-ble command:
uv run --extra cli tuya-ble scan
uv run --extra cli tuya-ble read \
--address AA:BB:CC:DD:EE:FF --device-id <id> --local-key <key>
scan lists every nearby Tuya BLE device with its product id and uuid; read
runs one session and prints the datapoints it reported. credentials logs into
a Tuya account and prints the device id and local key it holds for each device:
uv run --extra cli tuya-ble credentials --email you@example.com --country-code 55
Not implemented
The device may report datapoints in a signed form (0x8004 / 0x8005)
instead of the plain one this SDK reads. Those two commands are recognised and
logged, not parsed: the reference implementation disagrees with itself about
where the records start inside them, and no device was available to settle it.
A device that uses them shows up as a read that reports no datapoint, with the
command name in the debug log.
Development
uv sync # create .venv and install dependencies
uv run ruff format --check .
uv run ruff check .
uv run mypy src
uv run pytest # 90 % coverage gate
Runtime dependencies carry a >= floor and nothing else: Home Assistant pins its
own transitive dependencies exactly, so an == pin here eventually contradicts
HA's pin and the integration stops installing.
Credits
The protocol implementation is derived from
PlusPlus-ua/ha_tuya_ble (MIT),
itself based on redphx/poc-tuya-ble-fingerbot.
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tuya_ble_sdk-0.1.2.tar.gz.
File metadata
- Download URL: tuya_ble_sdk-0.1.2.tar.gz
- Upload date:
- Size: 141.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
71c36e53c8203cf608d7c49c28039dd8f1927a9e25eb181e769783900388ab03
|
|
| MD5 |
023f5615a2cb874d543c942ed5c83cc8
|
|
| BLAKE2b-256 |
b08d9f8880c3fdb116f4f0f725a627fba9f8e72440e4e5efa4007e8b4f34ea9d
|
File details
Details for the file tuya_ble_sdk-0.1.2-py3-none-any.whl.
File metadata
- Download URL: tuya_ble_sdk-0.1.2-py3-none-any.whl
- Upload date:
- Size: 41.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
855f3084cb2bf3c6bee22722631dca4c3165868e7afd587d21fb902174a59c56
|
|
| MD5 |
cf4e3f9e110493b176a1177d0e1abc0c
|
|
| BLAKE2b-256 |
93bf5877aad06cdb04602286f31fba7087d8fbfd0f4d18b4561f6b7e41ae225d
|