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

59 lines
2.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```bash
# 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:
```python
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).