Per-pack BMS queries (id arg): batSoc source + host_probe7

This commit is contained in:
kfj001 committed 2026-10-16 04:08:54 +00:00
1 parent 62e0fd649b
commit 3d59a75f14
5 files changed
+223 -13

No files matched your search

+20 -11
View File
@@ -30,7 +30,11 @@ log = logging.getLogger(__name__)
SOCKET_PORT_CANDIDATES: Tuple[int, ...] = (9000, 18000)
#: Reply keys searched for the aggregate percentage, in priority order.
SOC_KEYS: Tuple[str, ...] = ("currentBatterySoc", "soc", "batterySoc", "vppSoc")
#: ``batSoc`` is the per-pack charge from the BMS socket query
#: (``queryBmsInfoBySocket`` passes ``arguments={"id": pack}`` — without
#: the pack id the device answers the all-zero ``id: 0`` slot).
SOC_KEYS: Tuple[str, ...] = ("currentBatterySoc", "soc", "batSoc",
"batterySoc", "vppSoc")
def _as_dict(data: Any) -> Optional[Dict[str, Any]]:
@@ -131,12 +135,14 @@ def get_battery(
"""Query the aggregate charge percentage over the local TCP socket.
Mirrors the app's ``queryAPowerData`` first (``DEVICE_APOWER_INFO`` with
``data={"opt": 0, "update": 2}``), then ``BMS`` and ``EMS`` as fallbacks,
probing each candidate port. Like the app's direct-connect flow, a
discovery query (``QUERY_DEVICE_INFO``, empty serial) opens each port's
sequence — its reply also confirms two-way crypto before the battery
read. Returns the ``extract_soc`` dict plus ``cmd``/``port``/``reply``
context. Raises the last socket error if no port answers.
``data={"opt": 0, "update": 2}``), then per-pack ``BMS`` socket queries
(``queryBmsInfoBySocket`` passes the pack ``id`` — ``batSoc`` is the
pack charge), then ``EMS`` as a fallback, probing each candidate port.
Like the app's direct-connect flow, a discovery query
(``QUERY_DEVICE_INFO``, empty serial) opens each port's sequence — its
reply also confirms two-way crypto before the battery read. Returns
the ``extract_soc`` dict plus ``cmd``/``port``/``reply`` context.
Raises the last socket error if no port answers.
"""
last_err: Optional[Exception] = None
for port in ports:
@@ -150,11 +156,13 @@ def get_battery(
data={"minProtocolVer": "V1.00.00"})
except Exception as exc: # discovery is best-effort; keep going
log.debug("port %d discovery failed: %s", port, exc)
for cmd, data in (
attempts: Tuple[Tuple[CommunicationCmd, Any], ...] = (
(CommunicationCmd.DEVICE_APOWER_INFO, {"update": 2}),
(CommunicationCmd.BMS, None),
(CommunicationCmd.BMS, {"id": 1}),
(CommunicationCmd.BMS, {"id": 2}),
(CommunicationCmd.EMS, None),
):
)
for cmd, data in attempts:
try:
reply = client.send_rpc(cmd, data=data)
except (ConnectionError, socket.error, TimeoutError) as exc:
@@ -181,7 +189,8 @@ PROBE_VARIANTS: Tuple[Tuple[str, CommunicationCmd, Any], ...] = (
("apower/none", CommunicationCmd.DEVICE_APOWER_INFO, None),
("apower/empty", CommunicationCmd.DEVICE_APOWER_INFO, {}),
("queryDeviceInfo/none", CommunicationCmd.QUERY_DEVICE_INFO, None),
("bms/none", CommunicationCmd.BMS, None),
("bms/id1", CommunicationCmd.BMS, {"id": 1}),
("bms/id2", CommunicationCmd.BMS, {"id": 2}),
("ems/none", CommunicationCmd.EMS, None),
)
+8 -2
View File
@@ -285,8 +285,14 @@ class LocalDeviceClient:
def query_device_info(self) -> Dict[str, Any]:
return self.send_rpc(CommunicationCmd.QUERY_DEVICE_INFO)
def query_bms_info(self) -> Dict[str, Any]:
return self.send_rpc(CommunicationCmd.BMS)
def query_bms_info(self, pack_id: int = 1) -> Dict[str, Any]:
"""``queryBmsInfoBySocket`` — BMS pack query (``arguments={"id"}``).
The pack index selects the battery (``BMS_SN`` ``…B1``/``…B2`` in
the device-info reply); omitting it yields the all-zero ``id: 0``
slot. ``batSoc``/``batSoh`` carry the pack charge/health.
"""
return self.send_rpc(CommunicationCmd.BMS, data={"id": int(pack_id)})
def query_mppt_info(self) -> Dict[str, Any]:
return self.send_rpc(CommunicationCmd.MPPT)