"""River 2 protocol tests. Payload fixtures are synthetic, not hardware captures. Offsets follow ha-ef-ble's DirectPdHeartbeatPack, DirectEmsDeltaHeartbeatPack, and DirectBmsMDeltaHeartbeatPack. Fixtures do not use bmspy's field descriptors so an accidental layout change cannot silently change the test data too. """ import asyncio import importlib.util import struct import sys from argparse import Namespace from dataclasses import asdict from itertools import permutations from pathlib import Path from types import SimpleNamespace from unittest.mock import AsyncMock, Mock import pytest from bmspy.classes import UPS from bmspy.ecoflow_bt import ( EcoFlowBTDevice, EcoFlowBTManager, EcoFlowPacket, _EncFrameHelper, _RawHeaderHelper, _State, _Type1Encryption, _Type7Encryption, _crc8, _crc16, _enc_packet_to_bytes, _parse_enc_frame, collect_data, initialise_device, ) from bmspy.ecoflow_proto import decode_payload from bmspy.ecoflow_river2 import decode_river2_payload # Synthetic device identifiers, not values from a real device. ADDRESS = "02:00:00:00:00:01" SERIAL = "R601TEST00000001" def device(serial=SERIAL, **kwargs): return EcoFlowBTDevice(ADDRESS, serial, "test-user", **kwargs) def pd_payload(soc=73, watts_out=42, watts_in=160, remaining=87): data = bytearray(23) struct.pack_into("