Files
franklinwh-agate-client/docs/BATTERY_POC.md
T
kfj001 2438a04ed5 Add SoftAP battery PoC (apower/BMS/EMS socket read) with docs and tests
Probes 10.100.1.1:9000,18000, sends 0x19/0x1D/0x1E envelopes, extracts
currentBatterySoc/soc/avg(socArray). Includes offline extract_soc tests,
BATTERY_POC docs, README usage, and empirical port-9000 RE notes.
2026-10-15 01:26:28 +00:00

2.1 KiB
Raw Blame History

Battery PoC — direct-connect charge state

Reads the aggregate battery percentage straight from the aGate over its own SoftAP. No cloud, no MQTT, no Bluetooth. Recovered from the Dart AOT layer; see RE_DART_FINDINGS.md §§3–5, 8, 10.

Requirements

  • Host whose Wi-Fi adapter is associated to the device AP (AP_<sn[11:]> / <sn[8:]>, wifi.derive_hotspot_credentials).
  • L2 reachability to 10.100.1.1. In a container this means a bridged macvlan/ipvlan address on 10.100.1.0/24 or network_mode: host (see repo-root .devcontainer/docker-compose.yml). A default Docker bridge (172.17.0.0/16) is NATed to a non-10.100.1.x source and gets ECONNREFUSED.
  • Python requests for the HTTP helpers in device_local.py.

What it does

  1. Probes TCP ports 9000, 18000 in order (9000 was open on the tested unit; 0x4650 = 18000 is the app constant but was closed there).
  2. Sends DEVICE_APOWER_INFO (0x19), then BMS (0x1D), EMS (0x1E): compact-JSON CommunicationObject → encrypt_object (key hex(sum(sn) & 0xFF)) → int16LE length framing.
  3. Decrypts the reply and extracts currentBatterySoc → soc → avg(socArray).

Usage

# show derived hotspot creds only
python -m franklinwh_agate_client.battery --sn <20-char-SN> --show-hotspot

# print just the percentage
python -m franklinwh_agate_client.battery --sn <20-char-SN>

# full reply envelope
python -m franklinwh_agate_client.battery --sn <20-char-SN> --raw

Options: --host (default 10.100.1.1), --ports 9000,18000, --key-radix {16,32} (default 16; 32 is the alternate candidate in the disassembly), --timeout.

Programmatic:

from franklinwh_agate_client import get_battery
out = get_battery("...", host="10.100.1.1")
print(out["value"], out["source"])  # e.g. 78.0 currentBatterySoc

Limits

  • No live round-trip has succeeded yet from an isolated container netns (all probes ECONNREFUSED); first success needs a 10.100.1.x source.
  • key_radix, BLE field order, and X-Device-* binding remain open (RE_DART_FINDINGS.md Open items).