casambi-bt-revamped 0.3.12.dev3__py3-none-any.whl → 0.3.12.dev4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
CasambiBt/_client.py CHANGED
@@ -207,19 +207,23 @@ class CasambiClient:
207
207
  self._logger.info(f"Connected to {self.address}")
208
208
  self._connectionState = ConnectionState.CONNECTED
209
209
 
210
- # Detect protocol mode by available characteristics.
211
- services = await self._gattClient.get_services()
212
-
213
- def _has_char(uuid: str) -> bool:
214
- uuid_l = uuid.lower()
215
- for s in services:
216
- for c in s.characteristics:
217
- if c.uuid.lower() == uuid_l:
218
- return True
219
- return False
220
-
221
- # Classic (non-conformant) uses CA51 (connection hash) + CA52 (data channel).
222
- if _has_char(CASA_CLASSIC_HASH_CHAR_UUID) and _has_char(CASA_CLASSIC_DATA_CHAR_UUID):
210
+ # Detect protocol mode.
211
+ #
212
+ # Important: Home Assistant wraps BleakClient (HaBleakClientWrapper) which does not implement
213
+ # `get_services()`. Therefore we use "try-read" probing instead of enumerating GATT services.
214
+ #
215
+ # Order:
216
+ # 1) Classic "non-conformant": CA51 (hash) + CA52 (data channel)
217
+ # 2) EVO: auth char read starts with 0x01 (NodeInfo)
218
+ # 3) Classic "conformant": auth char read returns connection hash (first 8 bytes used)
219
+
220
+ classic_hash: bytes | None = None
221
+ try:
222
+ classic_hash = await self._gattClient.read_gatt_char(CASA_CLASSIC_HASH_CHAR_UUID)
223
+ except Exception:
224
+ classic_hash = None
225
+
226
+ if classic_hash and len(classic_hash) >= 8:
223
227
  if os.getenv("CASAMBI_BT_DISABLE_CLASSIC", "").strip() in {"1", "true", "TRUE", "yes", "YES"}:
224
228
  raise ProtocolError("Classic protocol detected but disabled via CASAMBI_BT_DISABLE_CLASSIC=1")
225
229
 
@@ -232,7 +236,7 @@ class CasambiClient:
232
236
  self._dataCharUuid = CASA_CLASSIC_DATA_CHAR_UUID
233
237
 
234
238
  # Read connection hash (first 8 bytes are used for CMAC signing).
235
- raw_hash = await self._gattClient.read_gatt_char(CASA_CLASSIC_HASH_CHAR_UUID)
239
+ raw_hash = classic_hash
236
240
  if raw_hash is None or len(raw_hash) < 8:
237
241
  raise ClassicHandshakeError(
238
242
  f"Classic connection hash read failed/too short (len={0 if raw_hash is None else len(raw_hash)})."
@@ -247,34 +251,48 @@ class CasambiClient:
247
251
  notify_params = inspect.signature(self._gattClient.start_notify).parameters
248
252
  if "bluez" in notify_params:
249
253
  notify_kwargs["bluez"] = {"use_start_notify": True}
250
- await self._gattClient.start_notify(
251
- CASA_CLASSIC_DATA_CHAR_UUID,
252
- self._queueCallback,
253
- **notify_kwargs,
254
- )
255
-
256
- # Classic has no EVO-style key exchange/auth; we can send immediately.
257
- self._connectionState = ConnectionState.AUTHENTICATED
258
- self._logger.info("Protocol mode selected: CLASSIC")
259
- if self._logger.isEnabledFor(logging.DEBUG):
260
- self._logger.debug(
261
- "[CASAMBI_CLASSIC_CONN_HASH] len=%d hash=%s",
262
- len(self._classicConnHash8),
263
- b2a(self._classicConnHash8),
254
+ try:
255
+ await self._gattClient.start_notify(
256
+ CASA_CLASSIC_DATA_CHAR_UUID,
257
+ self._queueCallback,
258
+ **notify_kwargs,
264
259
  )
265
- return
260
+ except Exception as e:
261
+ # Some firmwares may expose Classic signing on the EVO UUID instead.
262
+ # Fall through to auth-char probing if CA52 isn't available.
263
+ self._logger.debug("Classic CA52 notify failed; trying auth UUID probing.", exc_info=True)
264
+ self._protocolMode = None
265
+ self._dataCharUuid = None
266
+ self._classicConnHash8 = None
267
+ # continue detection below
268
+ else:
269
+ # Classic has no EVO-style key exchange/auth; we can send immediately.
270
+ self._connectionState = ConnectionState.AUTHENTICATED
271
+ self._logger.info("Protocol mode selected: CLASSIC")
272
+ if self._logger.isEnabledFor(logging.DEBUG):
273
+ self._logger.debug(
274
+ "[CASAMBI_CLASSIC_CONN_HASH] len=%d hash=%s",
275
+ len(self._classicConnHash8),
276
+ b2a(self._classicConnHash8),
277
+ )
278
+ return
266
279
 
267
280
  # Conformant devices can expose the Classic signed channel on the EVO-style UUID too.
268
- if _has_char(CASA_AUTH_CHAR_UUID):
281
+ first: bytes | None = None
282
+ try:
269
283
  first = await self._gattClient.read_gatt_char(CASA_AUTH_CHAR_UUID)
270
- if first and len(first) >= 2 and first[0] == 0x01:
271
- # EVO NodeInfo packet starts with 0x01.
272
- self._protocolMode = ProtocolMode.EVO
273
- self._dataCharUuid = CASA_AUTH_CHAR_UUID
274
- self._checkProtocolVersion(self._network.protocolVersion)
275
- self._logger.info("Protocol mode selected: EVO")
276
- return
284
+ except Exception:
285
+ first = None
286
+
287
+ if first and len(first) >= 2 and first[0] == 0x01:
288
+ # EVO NodeInfo packet starts with 0x01.
289
+ self._protocolMode = ProtocolMode.EVO
290
+ self._dataCharUuid = CASA_AUTH_CHAR_UUID
291
+ self._checkProtocolVersion(self._network.protocolVersion)
292
+ self._logger.info("Protocol mode selected: EVO")
293
+ return
277
294
 
295
+ if first is not None:
278
296
  # Otherwise, treat as Classic conformant: read provides connection hash.
279
297
  if os.getenv("CASAMBI_BT_DISABLE_CLASSIC", "").strip() in {"1", "true", "TRUE", "yes", "YES"}:
280
298
  raise ProtocolError("Classic protocol detected but disabled via CASAMBI_BT_DISABLE_CLASSIC=1")
@@ -282,9 +300,9 @@ class CasambiClient:
282
300
  raise ClassicKeysMissingError(
283
301
  "Classic protocol detected but network has no visitorKey/managerKey."
284
302
  )
285
- if first is None or len(first) < 8:
303
+ if len(first) < 8:
286
304
  raise ClassicHandshakeError(
287
- f"Classic connection hash read failed/too short (len={0 if first is None else len(first)})."
305
+ f"Classic connection hash read failed/too short (len={len(first)})."
288
306
  )
289
307
 
290
308
  self._protocolMode = ProtocolMode.CLASSIC
@@ -313,7 +331,7 @@ class CasambiClient:
313
331
  return
314
332
 
315
333
  raise ProtocolError(
316
- "No supported Casambi characteristics found (Classic ca51/ca52 or EVO/Classic conformant auth char)."
334
+ "No supported Casambi characteristics found (Classic ca51/ca52 or EVO/Classic-conformant auth char)."
317
335
  )
318
336
 
319
337
  def _on_disconnect(self, client: BleakClient) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: casambi-bt-revamped
3
- Version: 0.3.12.dev3
3
+ Version: 0.3.12.dev4
4
4
  Summary: Forked Casambi Bluetooth client library with switch event support, use original if no special need. https://github.com/lkempf/casambi-bt
5
5
  Home-page: https://github.com/rankjie/casambi-bt
6
6
  Author: rankjie
@@ -2,7 +2,7 @@ CasambiBt/__init__.py,sha256=TW445xSu5PV3TyMjJfwaA1JoWvQQ8LXhZgGdDTfWf3s,302
2
2
  CasambiBt/_cache.py,sha256=3bQil8vhSy4f4sf9JusMfEdQC7d3cJuva9qHhyKro-0,3808
3
3
  CasambiBt/_casambi.py,sha256=TN4ecgjm95nSJ4h9TsKayNn577Y82fdsGK4IGUZF23Q,40666
4
4
  CasambiBt/_classic_crypto.py,sha256=6DcCOdjLQo7k2cOOutNdUKupykOG_E2TDDwg6fH-ODM,998
5
- CasambiBt/_client.py,sha256=nOOvs9qyQdc8D37DRejOY-15iTcSolK2EAlOOvpg_Xo,49990
5
+ CasambiBt/_client.py,sha256=yTSuAeJhBXp5Zs3jU-RvHFEpI-quRNwlB3HWGl7q_yY,50730
6
6
  CasambiBt/_constants.py,sha256=sbElg5W8eeQvvL1rHn_E0jhP1wOrrabc7dFLLnlDMsU,810
7
7
  CasambiBt/_discover.py,sha256=jLc6H69JddrCURgtANZEjws6_UbSzXJtvJkbKTaIUHY,1849
8
8
  CasambiBt/_encryption.py,sha256=CLcoOOrggQqhJbnr_emBnEnkizpWDvb_0yFnitq4_FM,3831
@@ -14,8 +14,8 @@ CasambiBt/_switch_events.py,sha256=S8OD0dBcw5T4J2C7qfmOQMnTJ7omIXRUYv4PqDOB87E,1
14
14
  CasambiBt/_unit.py,sha256=KIpvUT_Wm-O2Lmb1JVnNO625-j5j7GqufmZzfTR-jW0,18587
15
15
  CasambiBt/errors.py,sha256=1L_Q8og_N_BRYEKizghAQXr6tihlHykFgtcCHUDcBas,1961
16
16
  CasambiBt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- casambi_bt_revamped-0.3.12.dev3.dist-info/licenses/LICENSE,sha256=TAIIitFxpxEDi6Iju7foW4TDQmWvC-IhLVLhl67jKmQ,11341
18
- casambi_bt_revamped-0.3.12.dev3.dist-info/METADATA,sha256=6oEPqnaAaaI5RZpDRNoquX7c9uHW742-J81FmPe1zNI,5877
19
- casambi_bt_revamped-0.3.12.dev3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
20
- casambi_bt_revamped-0.3.12.dev3.dist-info/top_level.txt,sha256=uNbqLjtecFosoFzpGAC89-5icikWODKI8rOjbi8v_sA,10
21
- casambi_bt_revamped-0.3.12.dev3.dist-info/RECORD,,
17
+ casambi_bt_revamped-0.3.12.dev4.dist-info/licenses/LICENSE,sha256=TAIIitFxpxEDi6Iju7foW4TDQmWvC-IhLVLhl67jKmQ,11341
18
+ casambi_bt_revamped-0.3.12.dev4.dist-info/METADATA,sha256=DKE1xb6Jg8lORTpoWyiM8qaSBOXOb5V_l7phDqWHGBA,5877
19
+ casambi_bt_revamped-0.3.12.dev4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
20
+ casambi_bt_revamped-0.3.12.dev4.dist-info/top_level.txt,sha256=uNbqLjtecFosoFzpGAC89-5icikWODKI8rOjbi8v_sA,10
21
+ casambi_bt_revamped-0.3.12.dev4.dist-info/RECORD,,