FD50 GATT fallback patch (vs ShonP40/Tuya-BLE master dabbf9e)

This commit is contained in:
2026-07-22 01:56:07 +03:00
parent 50a75e89e8
commit cb119294e3
+89
View File
@@ -0,0 +1,89 @@
diff --git a/custom_components/tuya_local_ble/tuya_ble/const.py b/custom_components/tuya_local_ble/tuya_ble/const.py
index b1514a9..c73f268 100644
--- a/custom_components/tuya_local_ble/tuya_ble/const.py
+++ b/custom_components/tuya_local_ble/tuya_ble/const.py
@@ -9,6 +9,12 @@ DEFAULT_ATTEMPTS = 0xFFFF
CHARACTERISTIC_NOTIFY = "00002b10-0000-1000-8000-00805f9b34fb"
CHARACTERISTIC_WRITE = "00002b11-0000-1000-8000-00805f9b34fb"
+# Newer TuyaOS firmware (e.g. jtmspro hc7n0urm fw 6.0) drops the legacy
+# 0xA201 service and exposes the data channel via these characteristics
+# of the 0xFD50 service instead.
+CHARACTERISTIC_NOTIFY_FD50 = "00000002-0000-1001-8001-00805f9b07d0"
+CHARACTERISTIC_WRITE_FD50 = "00000001-0000-1001-8001-00805f9b07d0"
+
SERVICE_UUID = "0000a201-0000-1000-8000-00805f9b34fb"
MANUFACTURER_DATA_ID = 0x07D0
diff --git a/custom_components/tuya_local_ble/tuya_ble/tuya_ble.py b/custom_components/tuya_local_ble/tuya_ble/tuya_ble.py
index 274cbf6..73049eb 100644
--- a/custom_components/tuya_local_ble/tuya_ble/tuya_ble.py
+++ b/custom_components/tuya_local_ble/tuya_ble/tuya_ble.py
@@ -24,7 +24,9 @@ from Crypto.Cipher import AES
from .const import (
CHARACTERISTIC_NOTIFY,
+ CHARACTERISTIC_NOTIFY_FD50,
CHARACTERISTIC_WRITE,
+ CHARACTERISTIC_WRITE_FD50,
GATT_MTU,
MANUFACTURER_DATA_ID,
RESPONSE_WAIT_TIMEOUT,
@@ -231,6 +233,9 @@ class TuyaBLEDevice:
self._current_seq_num = 1
self._seq_num_lock = asyncio.Lock()
+ self._characteristic_notify = CHARACTERISTIC_NOTIFY
+ self._characteristic_write = CHARACTERISTIC_WRITE
+
self._is_bound = False
self._flags = 0
self._protocol_version = 2
@@ -540,11 +545,25 @@ class TuyaBLEDevice:
self._expected_disconnect = True
self._client = None
if client and client.is_connected:
- await client.stop_notify(CHARACTERISTIC_NOTIFY)
+ await client.stop_notify(self._characteristic_notify)
await client.disconnect()
async with self._seq_num_lock:
self._current_seq_num = 1
+ def _select_characteristics(self, client: BleakClientWithServiceCache) -> None:
+ """Select the GATT channel actually exposed by the device."""
+ if client.services.get_characteristic(CHARACTERISTIC_NOTIFY):
+ self._characteristic_notify = CHARACTERISTIC_NOTIFY
+ self._characteristic_write = CHARACTERISTIC_WRITE
+ elif client.services.get_characteristic(CHARACTERISTIC_NOTIFY_FD50):
+ _LOGGER.debug(
+ "%s: legacy characteristics not present,"
+ " using FD50 GATT channel",
+ self.address,
+ )
+ self._characteristic_notify = CHARACTERISTIC_NOTIFY_FD50
+ self._characteristic_write = CHARACTERISTIC_WRITE_FD50
+
async def _ensure_connected(self) -> None:
"""Ensure connection to device is established."""
global global_connect_lock
@@ -609,9 +628,10 @@ class TuyaBLEDevice:
_LOGGER.debug("%s: Connected; RSSI: %s",
self.address, self.rssi)
self._client = client
+ self._select_characteristics(client)
try:
await self._client.start_notify(
- CHARACTERISTIC_NOTIFY,
+ self._characteristic_notify,
self._notification_handler,
bluez={"use_start_notify": True},
)
@@ -968,7 +988,7 @@ class TuyaBLEDevice:
try:
# _LOGGER.debug("%s: Sending packet: %s", self.address, packet.hex())
await self._client.write_gatt_char(
- CHARACTERISTIC_WRITE,
+ self._characteristic_write,
packet,
False,
)