Reconstruct the app's Dart-layer behaviour (blutter over libapp.so, jadx over base.apk — see docs/RE_DART_FINDINGS.md) as working Python, replacing the library stubs with a real local-connect stack. protocol/ (new package, hardware-independent): - commands: CommunicationMode / CommunicationOperate / CommunicationCmd enums with the raw values recovered from the Dart object pool - envelope: CommunicationObject with the app's exact JSON key order - crypto: SN-derived secret key and the position-dependent additive cipher (encrypt/decrypt; radix 16 default, 32 documented alternate) - crc: CRC-16/MODBUS with the app's hi/lo swap + 4-hex string form, CRC32 envelope check, little-endian int16 helpers - framing: BLE frame build/parse (0x5AA5 data / 0x4AA4 ACK magics, CRC slots, MTU chunking) and local-socket int16LE length framing plus a streaming SocketFrameDecoder wifi.py (rewritten): - derive_hotspot_credentials(): the AP_<sn> SSID/password rules (20-char SN branch, full-SN fallback with the app's warning log) - WifiBackend abstraction: NmcliWifiBackend for Linux hosts via nmcli, NoOpWifiBackend for radio-less containers - WifiManager keeps the original stub method names for API compat and adds join_device_hotspot(sn) / leave_device_hotspot() / local_ip() device_local.py (new): LocalDeviceClient for the mode==local path — the /terminal/* HTTP endpoint family (hotspot info, aGate info, overall info, MQTT relay, ...) and direct-connect socket RPC (envelope -> cipher -> length frame -> decrypt -> JSON) with convenience query_* RPCs. docs/RE_DART_FINDINGS.md (new): the full findings — transport selector, wire envelope, crypto, CRCs, MQTT (AWS IoT + Cognito), socket, BLE + ESP provisioning, hotspot credential derivation, local/cloud HTTP layers, and the Java glue — plus the open items; README now links to it. Unconfirmed candidates (local port 18000, cipher key radix, BLE field order, X-Device-* header values) are flagged in the docs and kept configurable in code.
57 lines
1.7 KiB
Python
57 lines
1.7 KiB
Python
"""CRC helpers recovered from ``communication_center/utils/crc16.dart`` / ``crc32.dart``.
|
|
|
|
See ``docs/RE_DART_FINDINGS.md`` §6.
|
|
|
|
* CRC16 = **CRC-16/MODBUS** (init ``0xFFFF``, reflected poly ``0xA001`` — the
|
|
disassembly shows the literal constant ``40961``).
|
|
* The app's ``Crc16::calculate`` returns the CRC with **hi/lo bytes swapped**,
|
|
rendered as a **4 lowercase hex digits** string.
|
|
* CRC32 (zlib/IEEE) is used for the JSON envelope integrity check.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import zlib
|
|
|
|
CRC16_INIT = 0xFFFF
|
|
CRC16_POLY = 0xA001 # reflected 0x8005 (CRC-16/MODBUS)
|
|
|
|
|
|
def crc16_modbus(data: bytes) -> int:
|
|
"""CRC-16/MODBUS over *data* (init 0xFFFF, reflected poly 0xA001)."""
|
|
crc = CRC16_INIT
|
|
for byte in data:
|
|
crc ^= byte
|
|
for _ in range(8):
|
|
if crc & 1:
|
|
crc = (crc >> 1) ^ CRC16_POLY
|
|
else:
|
|
crc >>= 1
|
|
return crc & 0xFFFF
|
|
|
|
|
|
def crc16_hex_string(data: bytes) -> str:
|
|
"""The app's ``Crc16::calculate`` equivalent.
|
|
|
|
CRC-16/MODBUS, hi/lo bytes swapped, 4 lowercase hex digits, e.g.
|
|
``crc16_modbus(b"123456789") == 0x4B37`` -> swapped ``0x374B`` -> ``"374b"``.
|
|
"""
|
|
crc = crc16_modbus(data)
|
|
swapped = ((crc & 0xFF) << 8) | (crc >> 8)
|
|
return f"{swapped:04x}"
|
|
|
|
|
|
def crc32(data: bytes) -> int:
|
|
"""CRC-32 (IEEE/zlib) — envelope integrity check."""
|
|
return zlib.crc32(data) & 0xFFFFFFFF
|
|
|
|
|
|
def int16_to_le_bytes(value: int) -> bytes:
|
|
"""``utils.int16ToBytes`` — little-endian 16-bit."""
|
|
return (value & 0xFFFF).to_bytes(2, "little")
|
|
|
|
|
|
def le_bytes_to_int16(data: bytes) -> int:
|
|
"""``utils.bytesToInt16`` — little-endian 16-bit."""
|
|
return int.from_bytes(data[:2], "little")
|