casambi-bt-revamped 0.3.12.dev10__py3-none-any.whl → 0.3.12.dev12__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/_casambi.py +6 -1
- CasambiBt/_client.py +279 -1
- CasambiBt/_version.py +1 -1
- {casambi_bt_revamped-0.3.12.dev10.dist-info → casambi_bt_revamped-0.3.12.dev12.dist-info}/METADATA +1 -1
- {casambi_bt_revamped-0.3.12.dev10.dist-info → casambi_bt_revamped-0.3.12.dev12.dist-info}/RECORD +8 -8
- {casambi_bt_revamped-0.3.12.dev10.dist-info → casambi_bt_revamped-0.3.12.dev12.dist-info}/WHEEL +0 -0
- {casambi_bt_revamped-0.3.12.dev10.dist-info → casambi_bt_revamped-0.3.12.dev12.dist-info}/licenses/LICENSE +0 -0
- {casambi_bt_revamped-0.3.12.dev10.dist-info → casambi_bt_revamped-0.3.12.dev12.dist-info}/top_level.txt +0 -0
CasambiBt/_casambi.py
CHANGED
|
@@ -169,10 +169,15 @@ class Casambi:
|
|
|
169
169
|
self._casaClient = cast(CasambiClient, self._casaClient)
|
|
170
170
|
await self._casaClient.connect()
|
|
171
171
|
try:
|
|
172
|
-
# EVO requires key exchange + authenticate; Classic is ready after `connect()`.
|
|
173
172
|
if self._casaClient.protocolMode == ProtocolMode.EVO:
|
|
173
|
+
# EVO requires key exchange + authenticate.
|
|
174
174
|
await self._casaClient.exchangeKey()
|
|
175
175
|
await self._casaClient.authenticate()
|
|
176
|
+
elif self._casaClient.protocolMode == ProtocolMode.CLASSIC:
|
|
177
|
+
# Classic needs an init write to trigger state broadcasts.
|
|
178
|
+
# In EVO the key exchange/auth handshake implicitly signals the
|
|
179
|
+
# device; Classic has no such handshake so we send a time-sync.
|
|
180
|
+
await self._casaClient.classicSendInit()
|
|
176
181
|
except ProtocolError as e:
|
|
177
182
|
await self._casaClient.disconnect()
|
|
178
183
|
raise e
|
CasambiBt/_client.py
CHANGED
|
@@ -159,6 +159,7 @@ class CasambiClient:
|
|
|
159
159
|
self._classicRxType9 = 0
|
|
160
160
|
self._classicRxCmdStream = 0
|
|
161
161
|
self._classicRxUnknown = 0
|
|
162
|
+
self._classicRxClassicStates = 0
|
|
162
163
|
# Per-kind sample counters to ensure we emit at least a few examples for reverse engineering.
|
|
163
164
|
self._classicRxKindSamples: dict[str, int] = {}
|
|
164
165
|
self._classicRxLastStatsTs = time.monotonic()
|
|
@@ -437,6 +438,7 @@ class CasambiClient:
|
|
|
437
438
|
self._network.classicManagerKey() is not None,
|
|
438
439
|
getattr(self._network, "isManager", lambda: False)(),
|
|
439
440
|
)
|
|
441
|
+
await self._classicEnumerateAndSubscribeGatt(notify_kwargs)
|
|
440
442
|
_log_probe_summary("CLASSIC", classic_variant="ca52_legacy")
|
|
441
443
|
# Emit a warning if we never see Classic RX frames; this is a common failure mode.
|
|
442
444
|
self._classicNoRxTask = asyncio.create_task(self._classic_no_rx_watchdog(30.0))
|
|
@@ -592,6 +594,7 @@ class CasambiClient:
|
|
|
592
594
|
self._network.classicManagerKey() is not None,
|
|
593
595
|
getattr(self._network, "isManager", lambda: False)(),
|
|
594
596
|
)
|
|
597
|
+
await self._classicEnumerateAndSubscribeGatt(notify_kwargs)
|
|
595
598
|
_log_probe_summary("CLASSIC", classic_variant="auth_uuid_conformant")
|
|
596
599
|
self._classicNoRxTask = asyncio.create_task(self._classic_no_rx_watchdog(30.0))
|
|
597
600
|
return
|
|
@@ -1222,6 +1225,132 @@ class CasambiClient:
|
|
|
1222
1225
|
len(pkt),
|
|
1223
1226
|
)
|
|
1224
1227
|
|
|
1228
|
+
async def _classicEnumerateAndSubscribeGatt(
|
|
1229
|
+
self, notify_kwargs: dict[str, Any]
|
|
1230
|
+
) -> None:
|
|
1231
|
+
"""Enumerate all GATT characteristics and subscribe to any notifiable ones.
|
|
1232
|
+
|
|
1233
|
+
This discovers characteristics beyond the manually-probed CA51/CA52/CA53
|
|
1234
|
+
UUIDs and subscribes to any that support notify or indicate, which may be
|
|
1235
|
+
needed for receiving Classic state/config notifications.
|
|
1236
|
+
"""
|
|
1237
|
+
try:
|
|
1238
|
+
total_chars = 0
|
|
1239
|
+
for svc in self._gattClient.services:
|
|
1240
|
+
for char in svc.characteristics:
|
|
1241
|
+
total_chars += 1
|
|
1242
|
+
char_uuid = str(char.uuid).lower()
|
|
1243
|
+
props = char.properties
|
|
1244
|
+
self._logger.warning(
|
|
1245
|
+
"[CASAMBI_CLASSIC_GATT_CHAR] uuid=%s props=%s handle=%d",
|
|
1246
|
+
char_uuid,
|
|
1247
|
+
props,
|
|
1248
|
+
char.handle,
|
|
1249
|
+
)
|
|
1250
|
+
if char_uuid not in self._classicNotifyCharUuids:
|
|
1251
|
+
if "notify" in props or "indicate" in props:
|
|
1252
|
+
try:
|
|
1253
|
+
await self._gattClient.start_notify(
|
|
1254
|
+
char.uuid,
|
|
1255
|
+
self._queueCallback,
|
|
1256
|
+
**notify_kwargs,
|
|
1257
|
+
)
|
|
1258
|
+
self._classicNotifyCharUuids.add(char_uuid)
|
|
1259
|
+
self._logger.warning(
|
|
1260
|
+
"[CASAMBI_CLASSIC_GATT_SUB] subscribed uuid=%s",
|
|
1261
|
+
char_uuid,
|
|
1262
|
+
)
|
|
1263
|
+
except Exception as e:
|
|
1264
|
+
self._logger.warning(
|
|
1265
|
+
"[CASAMBI_CLASSIC_GATT_SUB] failed uuid=%s err=%s",
|
|
1266
|
+
char_uuid,
|
|
1267
|
+
type(e).__name__,
|
|
1268
|
+
)
|
|
1269
|
+
self._logger.warning(
|
|
1270
|
+
"[CASAMBI_CLASSIC_GATT_ENUM] total_chars=%d subscribed_uuids=%s",
|
|
1271
|
+
total_chars,
|
|
1272
|
+
sorted(self._classicNotifyCharUuids),
|
|
1273
|
+
)
|
|
1274
|
+
except Exception as e:
|
|
1275
|
+
self._logger.warning(
|
|
1276
|
+
"[CASAMBI_CLASSIC_GATT_ENUM] services enumeration unavailable: %s",
|
|
1277
|
+
type(e).__name__,
|
|
1278
|
+
)
|
|
1279
|
+
|
|
1280
|
+
async def classicSendInit(self) -> None:
|
|
1281
|
+
"""Send Classic post-connection initialization (time-sync).
|
|
1282
|
+
|
|
1283
|
+
Ground truth: casambi-android AbstractC1717h.X() (lines 254-345).
|
|
1284
|
+
The Android app sends this as the first packet after Classic connection.
|
|
1285
|
+
In EVO, the key exchange/auth handshake implicitly signals the device;
|
|
1286
|
+
Classic has no such handshake, so an explicit init write is needed to
|
|
1287
|
+
trigger the device to start broadcasting state notifications.
|
|
1288
|
+
|
|
1289
|
+
The payload is sent raw via _sendClassic (NOT wrapped in buildClassicCommand).
|
|
1290
|
+
"""
|
|
1291
|
+
self._checkState(ConnectionState.AUTHENTICATED)
|
|
1292
|
+
if self._protocolMode != ProtocolMode.CLASSIC:
|
|
1293
|
+
return
|
|
1294
|
+
|
|
1295
|
+
import datetime as _dt
|
|
1296
|
+
|
|
1297
|
+
now = _dt.datetime.now()
|
|
1298
|
+
|
|
1299
|
+
# Timezone offset in minutes from UTC.
|
|
1300
|
+
local_tz = _dt.datetime.now(_dt.timezone.utc).astimezone().tzinfo
|
|
1301
|
+
utc_offset_minutes = 0
|
|
1302
|
+
if local_tz is not None:
|
|
1303
|
+
offset = local_tz.utcoffset(now)
|
|
1304
|
+
if offset is not None:
|
|
1305
|
+
utc_offset_minutes = int(offset.total_seconds()) // 60
|
|
1306
|
+
|
|
1307
|
+
# Build the time-sync payload.
|
|
1308
|
+
# Format: [10][year:2BE][month:1][day:1][hour:1][min:1][sec:1]
|
|
1309
|
+
# [tz_offset:2BE signed][dst_transition:4BE][dst_change:1]
|
|
1310
|
+
# [timestamp1:3BE][timestamp2:3BE][zero:2][millis:3BE][extra:1]
|
|
1311
|
+
payload = bytearray()
|
|
1312
|
+
payload.append(10) # Classic time-sync command byte
|
|
1313
|
+
payload.extend(struct.pack(">H", now.year))
|
|
1314
|
+
payload.append(now.month)
|
|
1315
|
+
payload.append(now.day)
|
|
1316
|
+
payload.append(now.hour)
|
|
1317
|
+
payload.append(now.minute)
|
|
1318
|
+
payload.append(now.second)
|
|
1319
|
+
payload.extend(struct.pack(">h", utc_offset_minutes))
|
|
1320
|
+
# DST transition data and change minutes (0 = no DST info).
|
|
1321
|
+
payload.extend(struct.pack(">I", 0))
|
|
1322
|
+
payload.append(0)
|
|
1323
|
+
# Classic extra bytes: timestamps, zero short, millis, trailing byte.
|
|
1324
|
+
# Android AbstractC1717h.X() lines 323-328: j() = 3-byte big-endian write
|
|
1325
|
+
# (Q2.t.java:59-63), NOT 4-byte. Plus trailing writeByte(iK0 >> 24).
|
|
1326
|
+
ts1 = 0 # Q2.r.K0(network.V) — start with 0
|
|
1327
|
+
ts2 = 0 # Q2.r.K0(network.W) — start with 0
|
|
1328
|
+
for ts in (ts1, ts2):
|
|
1329
|
+
payload.append((ts >> 16) & 0xFF)
|
|
1330
|
+
payload.append((ts >> 8) & 0xFF)
|
|
1331
|
+
payload.append(ts & 0xFF)
|
|
1332
|
+
payload.extend(struct.pack(">H", 0)) # writeShort(0)
|
|
1333
|
+
millis_val = now.microsecond // 1000 * 1000
|
|
1334
|
+
payload.append((millis_val >> 16) & 0xFF)
|
|
1335
|
+
payload.append((millis_val >> 8) & 0xFF)
|
|
1336
|
+
payload.append(millis_val & 0xFF)
|
|
1337
|
+
payload.append((ts1 >> 24) & 0xFF) # writeByte(iK0 >> 24)
|
|
1338
|
+
|
|
1339
|
+
self._logger.warning(
|
|
1340
|
+
"[CASAMBI_CLASSIC_INIT] sending time-sync len=%d hex=%s",
|
|
1341
|
+
len(payload),
|
|
1342
|
+
b2a(bytes(payload)),
|
|
1343
|
+
)
|
|
1344
|
+
|
|
1345
|
+
try:
|
|
1346
|
+
await self._sendClassic(bytes(payload))
|
|
1347
|
+
self._logger.warning("[CASAMBI_CLASSIC_INIT] time-sync sent successfully")
|
|
1348
|
+
except Exception:
|
|
1349
|
+
self._logger.warning(
|
|
1350
|
+
"[CASAMBI_CLASSIC_INIT] time-sync send failed",
|
|
1351
|
+
exc_info=True,
|
|
1352
|
+
)
|
|
1353
|
+
|
|
1225
1354
|
def _establishedNofityCallback(
|
|
1226
1355
|
self, handle: BleakGATTCharacteristic, data: bytes
|
|
1227
1356
|
) -> None:
|
|
@@ -1579,7 +1708,7 @@ class CasambiClient:
|
|
|
1579
1708
|
self._classicRxLastStatsTs = now
|
|
1580
1709
|
self._logger.warning(
|
|
1581
1710
|
"[CASAMBI_CLASSIC_RX_STATS] frames=%d verified=%d unverifiable=%d parse_fail=%d header=%s "
|
|
1582
|
-
"type6=%d type7=%d type9=%d cmdstream=%d unknown=%d",
|
|
1711
|
+
"type6=%d type7=%d type9=%d cmdstream=%d unknown=%d classic_states=%d",
|
|
1583
1712
|
self._classicRxFrames,
|
|
1584
1713
|
self._classicRxVerified,
|
|
1585
1714
|
self._classicRxUnverifiable,
|
|
@@ -1590,8 +1719,17 @@ class CasambiClient:
|
|
|
1590
1719
|
self._classicRxType9,
|
|
1591
1720
|
self._classicRxCmdStream,
|
|
1592
1721
|
self._classicRxUnknown,
|
|
1722
|
+
self._classicRxClassicStates,
|
|
1593
1723
|
)
|
|
1594
1724
|
|
|
1725
|
+
# Classic payloads use a completely different format from EVO.
|
|
1726
|
+
# Classic: byte 0 is a type indicator (0=netconfig, 255=log, else=unit_id).
|
|
1727
|
+
# EVO: byte 0 is a packet type (6=UnitState, 7=Switch, 9=NetConfig).
|
|
1728
|
+
# Dispatch Classic through its own parser to avoid misinterpretation.
|
|
1729
|
+
if self._protocolMode == ProtocolMode.CLASSIC:
|
|
1730
|
+
self._dispatchClassicPayload(payload)
|
|
1731
|
+
return
|
|
1732
|
+
|
|
1595
1733
|
# If the payload starts with a known EVO packet type, reuse existing parsers.
|
|
1596
1734
|
packet_type = payload[0]
|
|
1597
1735
|
if packet_type in (IncommingPacketType.UnitState, IncommingPacketType.SwitchEvent, IncommingPacketType.NetworkConfig):
|
|
@@ -1702,6 +1840,146 @@ class CasambiClient:
|
|
|
1702
1840
|
b2a(payload[pos:]),
|
|
1703
1841
|
)
|
|
1704
1842
|
|
|
1843
|
+
def _dispatchClassicPayload(self, payload: bytes) -> None:
|
|
1844
|
+
"""Dispatch a verified Classic payload based on its type indicator.
|
|
1845
|
+
|
|
1846
|
+
Classic payloads (from C1751c.V()) use a different format from EVO:
|
|
1847
|
+
- byte 0 == 0: network config data
|
|
1848
|
+
- byte 0 == 255: log message
|
|
1849
|
+
- otherwise: unit state stream (byte 0 is the first unit_id)
|
|
1850
|
+
"""
|
|
1851
|
+
if not payload:
|
|
1852
|
+
return
|
|
1853
|
+
|
|
1854
|
+
first_byte = payload[0]
|
|
1855
|
+
|
|
1856
|
+
# Log full payload for the first 10 Classic payloads regardless of type.
|
|
1857
|
+
if self._classicRxClassicStates < 10:
|
|
1858
|
+
self._logger.warning(
|
|
1859
|
+
"[CASAMBI_CLASSIC_DISPATCH] #%d type_byte=%d len=%d hex=%s",
|
|
1860
|
+
self._classicRxClassicStates,
|
|
1861
|
+
first_byte,
|
|
1862
|
+
len(payload),
|
|
1863
|
+
b2a(payload[: min(len(payload), 64)]).decode("ascii")
|
|
1864
|
+
+ ("..." if len(payload) > 64 else ""),
|
|
1865
|
+
)
|
|
1866
|
+
|
|
1867
|
+
if first_byte == 0:
|
|
1868
|
+
self._logger.debug("[CASAMBI_CLASSIC_NETCONFIG] len=%d", len(payload))
|
|
1869
|
+
return
|
|
1870
|
+
|
|
1871
|
+
if first_byte == 255:
|
|
1872
|
+
self._logger.debug("[CASAMBI_CLASSIC_LOG] len=%d", len(payload))
|
|
1873
|
+
return
|
|
1874
|
+
|
|
1875
|
+
# Unit state stream: entire payload is passed (first byte is the first unit_id).
|
|
1876
|
+
self._classicRxClassicStates += 1
|
|
1877
|
+
self._parseClassicUnitStates(payload)
|
|
1878
|
+
|
|
1879
|
+
def _parseClassicUnitStates(self, data: bytes) -> None:
|
|
1880
|
+
"""Parse Classic unit state records.
|
|
1881
|
+
|
|
1882
|
+
Ground truth: casambi-android C1751c.V() (line 301+).
|
|
1883
|
+
Format is completely different from EVO _parseUnitStates:
|
|
1884
|
+
- flags lower nibble = state_len (EVO uses a separate byte)
|
|
1885
|
+
- flags bit 5 = extra1 present, bit 6 = extra2 present, bit 7 = offline
|
|
1886
|
+
- unit_id 0xF0 = command response (skip)
|
|
1887
|
+
"""
|
|
1888
|
+
self._logger.debug("Parsing Classic unit states...")
|
|
1889
|
+
if self._logger.isEnabledFor(logging.DEBUG):
|
|
1890
|
+
self._logger.debug("[CASAMBI_CLASSIC_STATES_RAW] len=%d hex=%s", len(data), b2a(data))
|
|
1891
|
+
|
|
1892
|
+
pos = 0
|
|
1893
|
+
old_pos = 0
|
|
1894
|
+
records_parsed = 0
|
|
1895
|
+
try:
|
|
1896
|
+
while pos + 2 <= len(data):
|
|
1897
|
+
unit_id = data[pos]
|
|
1898
|
+
flags = data[pos + 1]
|
|
1899
|
+
pos += 2
|
|
1900
|
+
|
|
1901
|
+
state_len = flags & 0x0F
|
|
1902
|
+
has_extra1 = (flags & 0x20) != 0
|
|
1903
|
+
has_extra2 = (flags & 0x40) != 0
|
|
1904
|
+
is_offline = (flags & 0x80) != 0
|
|
1905
|
+
|
|
1906
|
+
# 0xF0 = command response record, skip state_len bytes.
|
|
1907
|
+
if unit_id == 0xF0:
|
|
1908
|
+
pos += state_len
|
|
1909
|
+
continue
|
|
1910
|
+
|
|
1911
|
+
extra1 = 0
|
|
1912
|
+
if has_extra1:
|
|
1913
|
+
if pos >= len(data):
|
|
1914
|
+
break
|
|
1915
|
+
extra1 = data[pos]
|
|
1916
|
+
pos += 1
|
|
1917
|
+
|
|
1918
|
+
extra2 = 0
|
|
1919
|
+
if has_extra2:
|
|
1920
|
+
if pos >= len(data):
|
|
1921
|
+
break
|
|
1922
|
+
extra2 = data[pos]
|
|
1923
|
+
pos += 1
|
|
1924
|
+
|
|
1925
|
+
if pos + state_len > len(data):
|
|
1926
|
+
break
|
|
1927
|
+
|
|
1928
|
+
state = data[pos : pos + state_len]
|
|
1929
|
+
pos += state_len
|
|
1930
|
+
records_parsed += 1
|
|
1931
|
+
|
|
1932
|
+
# Log the first few parsed records at WARNING level for tester visibility.
|
|
1933
|
+
if records_parsed <= 10 or self._logger.isEnabledFor(logging.DEBUG):
|
|
1934
|
+
self._logger.warning(
|
|
1935
|
+
"[CASAMBI_CLASSIC_STATE_PARSED] unit=%d flags=0x%02x state_len=%d "
|
|
1936
|
+
"offline=%s extra1=%d extra2=%d state=%s",
|
|
1937
|
+
unit_id,
|
|
1938
|
+
flags,
|
|
1939
|
+
state_len,
|
|
1940
|
+
is_offline,
|
|
1941
|
+
extra1,
|
|
1942
|
+
extra2,
|
|
1943
|
+
b2a(state),
|
|
1944
|
+
)
|
|
1945
|
+
|
|
1946
|
+
online = not is_offline
|
|
1947
|
+
# Let Unit.is_on derive actual on/off from state bytes (dimmer, onoff).
|
|
1948
|
+
on = True
|
|
1949
|
+
|
|
1950
|
+
self._dataCallback(
|
|
1951
|
+
IncommingPacketType.UnitState,
|
|
1952
|
+
{
|
|
1953
|
+
"id": unit_id,
|
|
1954
|
+
"online": online,
|
|
1955
|
+
"on": on,
|
|
1956
|
+
"state": state,
|
|
1957
|
+
"flags": flags,
|
|
1958
|
+
"prio": 0,
|
|
1959
|
+
"state_len": state_len,
|
|
1960
|
+
"padding_len": 0,
|
|
1961
|
+
"con": None,
|
|
1962
|
+
"sid": None,
|
|
1963
|
+
"extra_byte": extra1,
|
|
1964
|
+
"extra_float": extra1 / 255.0 if extra1 else 0.0,
|
|
1965
|
+
},
|
|
1966
|
+
)
|
|
1967
|
+
|
|
1968
|
+
old_pos = pos
|
|
1969
|
+
except IndexError:
|
|
1970
|
+
self._logger.error(
|
|
1971
|
+
"Ran out of data while parsing Classic unit state! Remaining data %s in %s.",
|
|
1972
|
+
b2a(data[old_pos:]),
|
|
1973
|
+
b2a(data),
|
|
1974
|
+
)
|
|
1975
|
+
|
|
1976
|
+
if records_parsed > 0:
|
|
1977
|
+
self._logger.debug(
|
|
1978
|
+
"[CASAMBI_CLASSIC_STATES_DONE] records=%d remaining=%d",
|
|
1979
|
+
records_parsed,
|
|
1980
|
+
len(data) - pos,
|
|
1981
|
+
)
|
|
1982
|
+
|
|
1705
1983
|
def _parseUnitStates(self, data: bytes) -> None:
|
|
1706
1984
|
# Ground truth: casambi-android `v1.C1775b.V(Q2.h)` parses decrypted packet type=6
|
|
1707
1985
|
# as a stream of unit state records. Records have optional bytes depending on flags.
|
CasambiBt/_version.py
CHANGED
{casambi_bt_revamped-0.3.12.dev10.dist-info → casambi_bt_revamped-0.3.12.dev12.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: casambi-bt-revamped
|
|
3
|
-
Version: 0.3.12.
|
|
3
|
+
Version: 0.3.12.dev12
|
|
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
|
{casambi_bt_revamped-0.3.12.dev10.dist-info → casambi_bt_revamped-0.3.12.dev12.dist-info}/RECORD
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
CasambiBt/__init__.py,sha256=iJdTF4oeXfj5d5gfGxQkacqUjtnQo0IW-zFPJvFjWWk,336
|
|
2
2
|
CasambiBt/_cache.py,sha256=3bQil8vhSy4f4sf9JusMfEdQC7d3cJuva9qHhyKro-0,3808
|
|
3
|
-
CasambiBt/_casambi.py,sha256=
|
|
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=a257JmcbYvdP0gSy-W1t3oqunA78dvhXMx-L_TMhr8o,95405
|
|
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=ai1o3EybsAhjyPohSOxeE0cWoFvEqdcc3PE3uFDaTfE,21346
|
|
|
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=BO8IqQymoBlsw14NOEBxAwMuuwVph3QNtmoRlFXHAXI,338
|
|
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.3.12.
|
|
19
|
-
casambi_bt_revamped-0.3.12.
|
|
20
|
-
casambi_bt_revamped-0.3.12.
|
|
21
|
-
casambi_bt_revamped-0.3.12.
|
|
22
|
-
casambi_bt_revamped-0.3.12.
|
|
18
|
+
casambi_bt_revamped-0.3.12.dev12.dist-info/licenses/LICENSE,sha256=TAIIitFxpxEDi6Iju7foW4TDQmWvC-IhLVLhl67jKmQ,11341
|
|
19
|
+
casambi_bt_revamped-0.3.12.dev12.dist-info/METADATA,sha256=xjFFDZYZ1zgTbiYSSelRo2BaT2pzSkmTlIBcpOG_qxY,5878
|
|
20
|
+
casambi_bt_revamped-0.3.12.dev12.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
21
|
+
casambi_bt_revamped-0.3.12.dev12.dist-info/top_level.txt,sha256=uNbqLjtecFosoFzpGAC89-5icikWODKI8rOjbi8v_sA,10
|
|
22
|
+
casambi_bt_revamped-0.3.12.dev12.dist-info/RECORD,,
|
{casambi_bt_revamped-0.3.12.dev10.dist-info → casambi_bt_revamped-0.3.12.dev12.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|