casambi-bt-revamped 0.3.12.dev15__py3-none-any.whl → 0.4.0__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 +52 -13
- CasambiBt/_version.py +1 -1
- {casambi_bt_revamped-0.3.12.dev15.dist-info → casambi_bt_revamped-0.4.0.dist-info}/METADATA +1 -1
- {casambi_bt_revamped-0.3.12.dev15.dist-info → casambi_bt_revamped-0.4.0.dist-info}/RECORD +7 -7
- {casambi_bt_revamped-0.3.12.dev15.dist-info → casambi_bt_revamped-0.4.0.dist-info}/WHEEL +0 -0
- {casambi_bt_revamped-0.3.12.dev15.dist-info → casambi_bt_revamped-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {casambi_bt_revamped-0.3.12.dev15.dist-info → casambi_bt_revamped-0.4.0.dist-info}/top_level.txt +0 -0
CasambiBt/_client.py
CHANGED
|
@@ -1386,7 +1386,7 @@ class CasambiClient:
|
|
|
1386
1386
|
# Android n0():998 uses WRITE_TYPE_NO_RESPONSE for classic.
|
|
1387
1387
|
response=False,
|
|
1388
1388
|
)
|
|
1389
|
-
self._logger.
|
|
1389
|
+
self._logger.debug("[CASAMBI_CLASSIC_INIT] version sent successfully")
|
|
1390
1390
|
except Exception:
|
|
1391
1391
|
self._logger.warning(
|
|
1392
1392
|
"[CASAMBI_CLASSIC_INIT] version send failed (continuing with time-sync)",
|
|
@@ -1486,7 +1486,7 @@ class CasambiClient:
|
|
|
1486
1486
|
# Android X():314 uses WRITE_TYPE_DEFAULT (2) = with-response.
|
|
1487
1487
|
response=True,
|
|
1488
1488
|
)
|
|
1489
|
-
self._logger.
|
|
1489
|
+
self._logger.debug("[CASAMBI_CLASSIC_INIT] time-sync sent successfully")
|
|
1490
1490
|
except Exception:
|
|
1491
1491
|
self._logger.warning(
|
|
1492
1492
|
"[CASAMBI_CLASSIC_INIT] time-sync send failed",
|
|
@@ -1601,7 +1601,7 @@ class CasambiClient:
|
|
|
1601
1601
|
except Exception:
|
|
1602
1602
|
handle_uuid = "unknown"
|
|
1603
1603
|
|
|
1604
|
-
self._logger.
|
|
1604
|
+
self._logger.debug(
|
|
1605
1605
|
"[CLASSIC_DIAG_RX] #%d handle=%s len=%d hex=%s",
|
|
1606
1606
|
self._classicRxFrames,
|
|
1607
1607
|
handle_uuid,
|
|
@@ -1618,12 +1618,45 @@ class CasambiClient:
|
|
|
1618
1618
|
|
|
1619
1619
|
if self._classicConnHash8 is None:
|
|
1620
1620
|
if self._logLimiter.allow("classic_rx_no_hash", burst=5, window_s=60.0):
|
|
1621
|
-
self._logger.
|
|
1621
|
+
self._logger.debug("[CASAMBI_CLASSIC_RX] missing_connection_hash len=%d", len(raw))
|
|
1622
1622
|
return
|
|
1623
1623
|
|
|
1624
1624
|
visitor_key = self._network.classicVisitorKey()
|
|
1625
1625
|
manager_key = self._network.classicManagerKey()
|
|
1626
1626
|
|
|
1627
|
+
def _walk_classic_records(data: bytes) -> bool:
|
|
1628
|
+
"""Check if data is a plausible Classic unit state record stream.
|
|
1629
|
+
|
|
1630
|
+
Walks the same record structure as _parseClassicUnitStates:
|
|
1631
|
+
- unit_id(1) + flags(1) + optional extras + state(state_len)
|
|
1632
|
+
- unit_id 0xF0 is a command response (no extras, state_len bytes consumed)
|
|
1633
|
+
Accepts if >=1 record parsed AND pos ends exactly at len(data).
|
|
1634
|
+
"""
|
|
1635
|
+
pos = 0
|
|
1636
|
+
count = 0
|
|
1637
|
+
while pos + 3 <= len(data): # Match Android available()>=3
|
|
1638
|
+
unit_id = data[pos]
|
|
1639
|
+
flags = data[pos + 1]
|
|
1640
|
+
state_len = flags & 0x0F
|
|
1641
|
+
# 0 and 255 are Classic control bytes, not valid unit records
|
|
1642
|
+
# inside a record stream (handled at dispatch level).
|
|
1643
|
+
if unit_id == 0 or unit_id == 255:
|
|
1644
|
+
return False
|
|
1645
|
+
pos += 2
|
|
1646
|
+
if unit_id == 0xF0:
|
|
1647
|
+
# Command response: cmd_id(1) + seq(1) + payload(state_len-2).
|
|
1648
|
+
if state_len < 2:
|
|
1649
|
+
return False
|
|
1650
|
+
pos += state_len
|
|
1651
|
+
else:
|
|
1652
|
+
has_extra1 = (flags & 0x20) != 0
|
|
1653
|
+
has_extra2 = (flags & 0x40) != 0
|
|
1654
|
+
pos += int(has_extra1) + int(has_extra2) + state_len
|
|
1655
|
+
if pos > len(data):
|
|
1656
|
+
return False
|
|
1657
|
+
count += 1
|
|
1658
|
+
return count >= 1 and pos == len(data)
|
|
1659
|
+
|
|
1627
1660
|
def _plausible_payload(payload: bytes) -> bool:
|
|
1628
1661
|
if not payload:
|
|
1629
1662
|
return False
|
|
@@ -1638,6 +1671,12 @@ class CasambiClient:
|
|
|
1638
1671
|
rec_len = (payload[0] - 239) & 0xFF
|
|
1639
1672
|
if 2 <= rec_len <= len(payload):
|
|
1640
1673
|
return True
|
|
1674
|
+
# Classic unit state record stream or control byte.
|
|
1675
|
+
if self._protocolMode == ProtocolMode.CLASSIC:
|
|
1676
|
+
if payload[0] in (0, 255):
|
|
1677
|
+
return True
|
|
1678
|
+
if _walk_classic_records(payload):
|
|
1679
|
+
return True
|
|
1641
1680
|
return False
|
|
1642
1681
|
|
|
1643
1682
|
def _score(verified: bool | None, payload: bytes) -> int:
|
|
@@ -1784,7 +1823,7 @@ class CasambiClient:
|
|
|
1784
1823
|
if not parsed_candidates:
|
|
1785
1824
|
self._classicRxParseFail += 1
|
|
1786
1825
|
if self._logLimiter.allow("classic_rx_parse_fail", burst=5, window_s=60.0):
|
|
1787
|
-
self._logger.
|
|
1826
|
+
self._logger.debug(
|
|
1788
1827
|
"[CASAMBI_CLASSIC_RX_PARSE_FAIL] len=%d prefix=%s",
|
|
1789
1828
|
len(raw),
|
|
1790
1829
|
b2a(raw[: min(len(raw), 32)]),
|
|
@@ -1808,7 +1847,7 @@ class CasambiClient:
|
|
|
1808
1847
|
if best["score"] == 0:
|
|
1809
1848
|
self._classicRxParseFail += 1
|
|
1810
1849
|
if self._logLimiter.allow("classic_rx_unplausible", burst=5, window_s=60.0):
|
|
1811
|
-
self._logger.
|
|
1850
|
+
self._logger.debug(
|
|
1812
1851
|
"[CASAMBI_CLASSIC_RX_UNPLAUSIBLE] preferred=%s len=%d prefix=%s",
|
|
1813
1852
|
preferred,
|
|
1814
1853
|
len(raw),
|
|
@@ -1842,7 +1881,7 @@ class CasambiClient:
|
|
|
1842
1881
|
self._classicRxHistory = self._classicRxHistory[-self._classicDiagMaxHistory:]
|
|
1843
1882
|
|
|
1844
1883
|
# Enhanced RX parse result log
|
|
1845
|
-
self._logger.
|
|
1884
|
+
self._logger.debug(
|
|
1846
1885
|
"[CLASSIC_DIAG_RX_PARSE] mode=%s verified=%s auth=%s sig_len=%d seq=%s score=%d payload_len=%d",
|
|
1847
1886
|
best["mode"],
|
|
1848
1887
|
verified,
|
|
@@ -1858,7 +1897,7 @@ class CasambiClient:
|
|
|
1858
1897
|
if best["mode"] != preferred and best["mode"] in ("conformant", "legacy"):
|
|
1859
1898
|
# Only switch if we got a stronger signal (verified or plausible payload with fewer assumptions).
|
|
1860
1899
|
if best["score"] >= 50 and self._logLimiter.allow("classic_rx_mode_switch", burst=3, window_s=3600.0):
|
|
1861
|
-
self._logger.
|
|
1900
|
+
self._logger.debug(
|
|
1862
1901
|
"[CASAMBI_CLASSIC_RX_MODE] switching %s -> %s (score=%d verified=%s sig_len=%d)",
|
|
1863
1902
|
preferred,
|
|
1864
1903
|
best["mode"],
|
|
@@ -1870,7 +1909,7 @@ class CasambiClient:
|
|
|
1870
1909
|
|
|
1871
1910
|
# Sample RX logs (limited) + periodic stats (limited).
|
|
1872
1911
|
if self._logLimiter.allow("classic_rx_sample", burst=10, window_s=60.0):
|
|
1873
|
-
self._logger.
|
|
1912
|
+
self._logger.debug(
|
|
1874
1913
|
"[CASAMBI_CLASSIC_RX] header=%s verified=%s auth=%s sig_len=%d seq=%s payload_prefix=%s",
|
|
1875
1914
|
best["mode"],
|
|
1876
1915
|
verified,
|
|
@@ -1884,7 +1923,7 @@ class CasambiClient:
|
|
|
1884
1923
|
"classic_rx_stats", burst=2, window_s=60.0
|
|
1885
1924
|
):
|
|
1886
1925
|
self._classicRxLastStatsTs = now
|
|
1887
|
-
self._logger.
|
|
1926
|
+
self._logger.debug(
|
|
1888
1927
|
"[CASAMBI_CLASSIC_RX_STATS] frames=%d verified=%d unverifiable=%d parse_fail=%d header=%s "
|
|
1889
1928
|
"type6=%d type7=%d type9=%d cmdstream=%d unknown=%d classic_states=%d",
|
|
1890
1929
|
self._classicRxFrames,
|
|
@@ -2000,7 +2039,7 @@ class CasambiClient:
|
|
|
2000
2039
|
|
|
2001
2040
|
if self._classicRxKindSamples.get(kind, 0) < 3:
|
|
2002
2041
|
self._classicRxKindSamples[kind] = self._classicRxKindSamples.get(kind, 0) + 1
|
|
2003
|
-
self._logger.
|
|
2042
|
+
self._logger.debug(
|
|
2004
2043
|
"[CASAMBI_CLASSIC_RX_KIND] kind=%s header=%s verified=%s sig_len=%d seq=%s payload_prefix=%s",
|
|
2005
2044
|
kind,
|
|
2006
2045
|
best["mode"],
|
|
@@ -2033,7 +2072,7 @@ class CasambiClient:
|
|
|
2033
2072
|
|
|
2034
2073
|
# Log full payload for the first 10 Classic payloads regardless of type.
|
|
2035
2074
|
if self._classicRxClassicStates < 10:
|
|
2036
|
-
self._logger.
|
|
2075
|
+
self._logger.debug(
|
|
2037
2076
|
"[CASAMBI_CLASSIC_DISPATCH] #%d type_byte=%d len=%d hex=%s",
|
|
2038
2077
|
self._classicRxClassicStates,
|
|
2039
2078
|
first_byte,
|
|
@@ -2121,7 +2160,7 @@ class CasambiClient:
|
|
|
2121
2160
|
|
|
2122
2161
|
# Log the first few parsed records at WARNING level for tester visibility.
|
|
2123
2162
|
if records_parsed <= 10 or self._logger.isEnabledFor(logging.DEBUG):
|
|
2124
|
-
self._logger.
|
|
2163
|
+
self._logger.debug(
|
|
2125
2164
|
"[CASAMBI_CLASSIC_STATE_PARSED] unit=%d flags=0x%02x state_len=%d "
|
|
2126
2165
|
"online=%s extra1=%d extra2=%d state=%s",
|
|
2127
2166
|
unit_id,
|
CasambiBt/_version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: casambi-bt-revamped
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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=iJdTF4oeXfj5d5gfGxQkacqUjtnQo0IW-zFPJvFjWWk,336
|
|
|
2
2
|
CasambiBt/_cache.py,sha256=3bQil8vhSy4f4sf9JusMfEdQC7d3cJuva9qHhyKro-0,3808
|
|
3
3
|
CasambiBt/_casambi.py,sha256=dAZZ0S2-t2ShLbW78AE9lOLBzOmhBOTTXJky-6khdkE,41981
|
|
4
4
|
CasambiBt/_classic_crypto.py,sha256=XIp3JBaeY8hIUv5kB0ygVG_eRx9AgHHF4ts2--CFm78,4973
|
|
5
|
-
CasambiBt/_client.py,sha256=
|
|
5
|
+
CasambiBt/_client.py,sha256=MusVDaJ4fUMfhn8bwdEe7WW9HY67Uds_Ohavpi1vECk,105803
|
|
6
6
|
CasambiBt/_constants.py,sha256=86heoDdb5iPaRrPmK2DIIl-4uSxbFFcnCo9zlCvTLww,1290
|
|
7
7
|
CasambiBt/_discover.py,sha256=jLc6H69JddrCURgtANZEjws6_UbSzXJtvJkbKTaIUHY,1849
|
|
8
8
|
CasambiBt/_encryption.py,sha256=CLcoOOrggQqhJbnr_emBnEnkizpWDvb_0yFnitq4_FM,3831
|
|
@@ -12,11 +12,11 @@ CasambiBt/_network.py,sha256=3ZUedQlHzzuHHiG5KxDLnK0AIz0TjzG1_vwg0UGsO9U,22132
|
|
|
12
12
|
CasambiBt/_operation.py,sha256=Q5UccsrtNp_B_wWqwH_3eLFW_yF6A55FMmfUKDk2WrI,1059
|
|
13
13
|
CasambiBt/_switch_events.py,sha256=S8OD0dBcw5T4J2C7qfmOQMnTJ7omIXRUYv4PqDOB87E,13137
|
|
14
14
|
CasambiBt/_unit.py,sha256=nxbg_8UCCVB9WI8dUS21g2JrGyPKcefqKMSusMOhLOo,18721
|
|
15
|
-
CasambiBt/_version.py,sha256=
|
|
15
|
+
CasambiBt/_version.py,sha256=2uX0-jxYxOYPO9Zyusle5-xRIXdXU2JXo5Tloqx9j7k,331
|
|
16
16
|
CasambiBt/errors.py,sha256=1L_Q8og_N_BRYEKizghAQXr6tihlHykFgtcCHUDcBas,1961
|
|
17
17
|
CasambiBt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
casambi_bt_revamped-0.
|
|
19
|
-
casambi_bt_revamped-0.
|
|
20
|
-
casambi_bt_revamped-0.
|
|
21
|
-
casambi_bt_revamped-0.
|
|
22
|
-
casambi_bt_revamped-0.
|
|
18
|
+
casambi_bt_revamped-0.4.0.dist-info/licenses/LICENSE,sha256=TAIIitFxpxEDi6Iju7foW4TDQmWvC-IhLVLhl67jKmQ,11341
|
|
19
|
+
casambi_bt_revamped-0.4.0.dist-info/METADATA,sha256=H0L3tsjrS_Buu3UdUtGFq32ct5ZUSdFdLfh4ju9GsSc,5871
|
|
20
|
+
casambi_bt_revamped-0.4.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
21
|
+
casambi_bt_revamped-0.4.0.dist-info/top_level.txt,sha256=uNbqLjtecFosoFzpGAC89-5icikWODKI8rOjbi8v_sA,10
|
|
22
|
+
casambi_bt_revamped-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
{casambi_bt_revamped-0.3.12.dev15.dist-info → casambi_bt_revamped-0.4.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{casambi_bt_revamped-0.3.12.dev15.dist-info → casambi_bt_revamped-0.4.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|