import unittest from berger_gui import DeviceState, berger_match_reason, decode_response, read_frame, split_frames, write_frame 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_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") body = bytes([0xDD, 0x03, 0x00, len(payload), *payload]) total = sum(body[1:]) & 0xFFFF check = ((total ^ 0xFFFF) + 1) & 0xFFFF frame = body + check.to_bytes(2, "big") + b"\x77" 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]) body = bytes([0xDD, 0x04, 0x00, len(payload), *payload]) total = sum(body[1:]) & 0xFFFF check = ((total ^ 0xFFFF) + 1) & 0xFFFF frame = body + check.to_bytes(2, "big") + b"\x77" state = DeviceState() decode_response(frame, state) self.assertEqual(state.telemetry.cells, [3.306, 3.312, 3.308, 3.311]) 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()