Commit Graph
3 Commits
Author SHA1 Message Date
kfj001 b60b32830f Optional aGate address on the API (home-LAN direct connect)
AgateClient takes address= default; attach_local accepts
address/host/gateway_ip per call with join control. An explicit
address skips the SoftAP radio join by default (join=True forces
it). derive_hotspot_credentials and join_device_hotspot accept a
gateway_ip override. battery/power host defaults now use
DEVICE_GATEWAY_IP. Verified live at 192.168.1.142:9000.
2026-10-16 08:18:33 +00:00
kfj001 4c0b9de3df Make Wi-Fi join/leave actually functional
wifi.py presented itself as a join/leave abstraction but provided no real
behavior: the default backend was a no-op, the facade never verified the
association, and the nmcli queries were wrong. This makes the module do what
it claims, verified end-to-end against the recovered protocol and nmcli 1.52.

wifi.py
  - WifiManager: join now verifies the association by polling
    backend.is_connected against a settle_timeout (fail-closed). An
    unverified join raises HotspotJoinError and leaves connected=False;
    verify=False restores fire-and-forget. Tracks last_profile and adds
    gateway_ip().
  - NmcliWifiBackend: the association check now uses the ACTIVE column
    (`device wifi list -f SSID,ACTIVE`). The old `device wifi show` was a
    prefix-match accident onto `show-password` and reported in-range APs, not
    the associated one. get_ip() queries per-Wi-Fi-interface IP4.ADDRESS
    (preferring the 10.100 aGate subnet) instead of the unfiltered all-device
    dump that would return a wired/VPN address. Signal is read from the active
    AP; the dead ipcmd param is removed; leave() is best-effort.
  - WifiBackend: documented the fail-closed contract and added get_signal()
    so the manager no longer reaches into a private _run via isinstance.
  - NoOpWifiBackend: added assume_connected() for manual/external joins.

client.py
  - AgateClient now owns a WifiManager and exposes join_hotspot(),
    leave_hotspot(), and attach_local() (join + verify + LocalDeviceClient
    aimed at the device gateway). LocalDeviceClient is imported lazily so the
    package still works without `requests`.

__init__.py
  - Export the backends, HotspotProfile, HotspotJoinError, the SN-derivation
    helper, and the module constants.

tests/test_wifi.py (new)
  - 20 stdlib-unittest cases: SN derivation, verify pass/fail, fail-closed
    behavior, the in-range-but-not-associated regression, IP preference,
    tolerant leave, and the client wiring. No radio or pytest required.

Also:
  - README: documented the local-connect (device hotspot) flow.
  - .gitignore: ignore Python build/cache artifacts.
2026-10-13 02:27:43 +00:00
robot d48b6b17c7 Initial commit: FranklinWH AGATE client library reverse engineered from Family Energy app
- Reverse engineered network I/O stack for AGATE device
- Bluetooth LE and Wi-Fi onboard communication support
- Discovered operations: queryAgateInfo, queryDeviceInfoHttp, queryModesByHttp, queryConnectWifi, connectViaBluetooth, startBluetoothHeartbeat
- Cloud API endpoints identified: e-api.franklinwh.com, energy.franklinwh.com, hes-gateway
- Python library structure with client, bluetooth, wifi, api modules
- Documentation for reverse engineering findings
2026-10-10 02:46:11 +00:00