123 lines
4.8 KiB
Python
123 lines
4.8 KiB
Python
import unittest
|
|
|
|
from berger_gui import (
|
|
DeviceState,
|
|
berger_match_reason,
|
|
cell_statistics,
|
|
decode_response,
|
|
mos_mode,
|
|
read_frame,
|
|
split_frames,
|
|
validate_frame,
|
|
write_frame,
|
|
)
|
|
|
|
|
|
def response_frame(command, payload):
|
|
body = bytes([0xDD, command, 0x00, len(payload), *payload])
|
|
check = (-sum(body[2:])) & 0xFFFF
|
|
return body + check.to_bytes(2, "big") + b"\x77"
|
|
|
|
|
|
class BergerParserTest(unittest.TestCase):
|
|
def test_read_frame_checksum(self):
|
|
self.assertEqual(read_frame(0x03).hex().upper(), "DDA50300FFFD77")
|
|
self.assertEqual(read_frame(0x04).hex().upper(), "DDA50400FFFC77")
|
|
|
|
def test_write_switch_checksum(self):
|
|
self.assertEqual(write_frame(0xE1, b"\x00\x00").hex().upper(), "DD5AE1020000FF1D77")
|
|
|
|
def test_mos_modes(self):
|
|
self.assertEqual(mos_mode(True, True), 0)
|
|
self.assertEqual(mos_mode(False, True), 1)
|
|
self.assertEqual(mos_mode(True, False), 2)
|
|
self.assertEqual(mos_mode(False, False), 3)
|
|
|
|
def test_decode_base_data(self):
|
|
payload = bytearray(27 + 4)
|
|
payload[0:2] = (1328).to_bytes(2, "big")
|
|
payload[2:4] = (65536 - 752).to_bytes(2, "big")
|
|
payload[4:6] = (10750).to_bytes(2, "big")
|
|
payload[6:8] = (16000).to_bytes(2, "big")
|
|
payload[8:10] = (3).to_bytes(2, "big")
|
|
payload[10:12] = ((25 << 9) | (7 << 5) | 27).to_bytes(2, "big")
|
|
payload[16:18] = (0x0010).to_bytes(2, "big")
|
|
payload[18] = 21
|
|
payload[19] = 67
|
|
payload[20] = 3
|
|
payload[22] = 2
|
|
payload[23:25] = (2981).to_bytes(2, "big")
|
|
payload[25:27] = (2991).to_bytes(2, "big")
|
|
frame = response_frame(0x03, payload)
|
|
|
|
buffer = bytearray(b"\x00" + frame + frame)
|
|
frames = split_frames(buffer)
|
|
self.assertEqual(len(frames), 2)
|
|
|
|
state = DeviceState()
|
|
decode_response(frames[0], state)
|
|
self.assertAlmostEqual(state.telemetry.voltage, 13.28)
|
|
self.assertAlmostEqual(state.telemetry.current, -7.52)
|
|
self.assertEqual(state.telemetry.soc, 67)
|
|
self.assertAlmostEqual(state.telemetry.remaining_capacity, 107.5)
|
|
self.assertAlmostEqual(state.telemetry.nominal_capacity, 160.0)
|
|
self.assertEqual(state.telemetry.cycle_count, 3)
|
|
self.assertEqual(state.telemetry.production_date, "2025-7-27")
|
|
self.assertTrue(state.telemetry.charge_mos)
|
|
self.assertTrue(state.telemetry.discharge_mos)
|
|
self.assertTrue(state.telemetry.protection)
|
|
self.assertEqual(state.telemetry.temperatures, [25.0, 26.0])
|
|
self.assertEqual(state.telemetry.time_label, "Time till empty")
|
|
|
|
def test_decode_cell_voltages(self):
|
|
payload = b"".join(v.to_bytes(2, "big") for v in [3306, 3312, 3308, 3311])
|
|
frame = response_frame(0x04, payload)
|
|
state = DeviceState()
|
|
decode_response(frame, state)
|
|
self.assertEqual(state.telemetry.cells, [3.306, 3.312, 3.308, 3.311])
|
|
|
|
def test_captured_fragmented_base_data(self):
|
|
chunks = [
|
|
bytes.fromhex("DD030026053400005F8C75300000348900000000"),
|
|
bytes.fromhex("000063520304030BB70BA60BAA00000075305F8C"),
|
|
bytes.fromhex("0000F8DD77"),
|
|
]
|
|
buffer = bytearray()
|
|
frames = []
|
|
for chunk in chunks:
|
|
buffer.extend(chunk)
|
|
frames.extend(split_frames(buffer))
|
|
|
|
self.assertEqual(len(frames), 1)
|
|
self.assertTrue(validate_frame(frames[0]))
|
|
state = DeviceState()
|
|
decode_response(frames[0], state)
|
|
self.assertEqual(state.telemetry.voltage, 13.32)
|
|
self.assertEqual(state.telemetry.remaining_capacity, 244.6)
|
|
self.assertEqual(state.telemetry.nominal_capacity, 300.0)
|
|
self.assertEqual(state.telemetry.soc, 82)
|
|
self.assertEqual(state.telemetry.production_date, "2026-4-9")
|
|
self.assertEqual(state.telemetry.temperatures, [26.8, 25.1, 25.5])
|
|
self.assertTrue(state.telemetry.charge_mos)
|
|
self.assertTrue(state.telemetry.discharge_mos)
|
|
|
|
def test_captured_cell_voltages(self):
|
|
frame = bytes.fromhex("DD0400080D030D040D040D04FFB577")
|
|
self.assertTrue(validate_frame(frame))
|
|
state = DeviceState()
|
|
decode_response(frame, state)
|
|
self.assertEqual(state.telemetry.cells, [3.331, 3.332, 3.332, 3.332])
|
|
self.assertEqual(cell_statistics(state.telemetry.cells), (3.331, 3.332, 0.001))
|
|
|
|
def test_empty_cell_statistics(self):
|
|
self.assertIsNone(cell_statistics([]))
|
|
|
|
def test_scan_filter(self):
|
|
self.assertEqual(berger_match_reason(None, ["0000ff00-0000-1000-8000-00805f9b34fb"]), "service:ff00")
|
|
self.assertEqual(berger_match_reason("JBD-SP04S", []), "name")
|
|
self.assertIsNone(berger_match_reason("Keyboard", []))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|