Wait for the required EcoFlow reports in each collection
A Bluetooth collection listened for a fixed 10 seconds and returned the data that arrived. If a required report was late, the snapshot missed shared measurements without notice. Each decoder now tracks the reports behind the shared measurements. A collection listens for 10 seconds, then until those reports arrive, for at most 30 seconds in total. The new ef_missing_measurements field names the reports that did not arrive. The server log shows one line each time that list changes. The diagnostic tool now uses the River 2 heartbeat list from the decoder.
This commit is contained in:
@@ -152,6 +152,50 @@ def test_shared_power_and_temperature_measurements_are_floats(decoder_type, pack
|
||||
assert [type(value) for value in values] == [float, float, float]
|
||||
|
||||
|
||||
def test_river2_collection_is_complete_after_the_three_core_heartbeats():
|
||||
decoder = ecoflow_river2.River2Decoder()
|
||||
snapshot = EcoFlowSnapshot()
|
||||
assert decoder.missing() == {"PD heartbeat", "EMS heartbeat", "BMS heartbeat"}
|
||||
decoder.update(snapshot, *PD_PACKET)
|
||||
decoder.update(snapshot, 4, 0x20, 2, bytes(40)) # INV is optional.
|
||||
decoder.update(snapshot, 3, 0x20, 0x32, bytes(5)) # A short BMS heartbeat is rejected.
|
||||
assert decoder.missing() == {"EMS heartbeat", "BMS heartbeat"}
|
||||
# 5999 marks an unavailable estimate. Its heartbeat still counts as received.
|
||||
decoder.update(snapshot, 3, 0x20, 2, bytes(17) + struct.pack("<II", 5999, 5999))
|
||||
decoder.update(snapshot, 3, 0x20, 0x32, bytes(20) + b"\x1c")
|
||||
assert snapshot.ef_charge_remaining_time_minutes is None
|
||||
assert decoder.missing() == set()
|
||||
decoder.reset()
|
||||
assert len(decoder.missing()) == 3
|
||||
|
||||
|
||||
def test_river3_collection_is_complete_across_partial_reports():
|
||||
def field(number, value):
|
||||
tag = number << 3 | (5 if isinstance(value, float) else 0)
|
||||
encoded = bytearray()
|
||||
for part in (tag, None if isinstance(value, float) else value):
|
||||
while part is not None:
|
||||
encoded.append(part & 0x7F | (0x80 if part > 0x7F else 0))
|
||||
part = part >> 7 or None
|
||||
return bytes(encoded) + (struct.pack("<f", value) if isinstance(value, float) else b"")
|
||||
|
||||
decoder = ecoflow_river3.River3Decoder()
|
||||
snapshot = EcoFlowSnapshot()
|
||||
assert len(decoder.missing()) == len(ecoflow_river3.REQUIRED_FIELDS)
|
||||
first = field(262, 50.0) + field(3, 244.0) + field(4, 39.0) + field(158, -39.0)
|
||||
decoder.update(snapshot, 2, 0xFE, 0x15, first)
|
||||
assert decoder.missing() == {
|
||||
"bms_max_cell_temp or cms_batt_temp", "cms_chg_rem_time", "cms_dsg_rem_time",
|
||||
}
|
||||
# Either temperature will do, and an unavailable estimate still counts as received.
|
||||
second = field(102, 20) + field(268, 0xFFFFFFFF) + field(269, 0xFFFFFFFF)
|
||||
decoder.update(snapshot, 2, 0xFE, 0x15, second)
|
||||
assert snapshot.ef_charge_remaining_time_minutes is None
|
||||
assert decoder.missing() == set()
|
||||
decoder.reset()
|
||||
assert len(decoder.missing()) == len(ecoflow_river3.REQUIRED_FIELDS)
|
||||
|
||||
|
||||
def test_snapshot_keeps_diagnostic_packets_and_json_round_trip():
|
||||
from bmspy.classes import UPS
|
||||
|
||||
|
||||
Reference in New Issue
Block a user