Add MOS gate control via select entity
Adds a Select entity with four options (Normal / Charge Disabled / Discharge Disabled / Both Disabled) that sends the 0xE1 write command to the BMS and immediately refreshes sensor state. Current option is derived from the mos_charge_enabled / mos_discharge_enabled bits already parsed on every poll. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -161,6 +161,53 @@ class BmsBluetoothHandler:
|
||||
|
||||
return None
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# MOS write command
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
async def write_mos(self, ble_device: BLEDevice, value: int) -> bool:
|
||||
"""Send a MOS control write command and return True on ACK.
|
||||
|
||||
Follows the same connect → send → disconnect pattern as poll() so
|
||||
it doesn't interfere with the normal poll cycle.
|
||||
"""
|
||||
command = self._build_mos_command(value)
|
||||
_LOGGER.debug("Writing MOS value 0x%02X to BMS at %s", value, self._address)
|
||||
client = BleakClient(ble_device)
|
||||
try:
|
||||
await client.connect()
|
||||
await client.start_notify(RX_CHAR_UUID, self._on_notify)
|
||||
await asyncio.sleep(0.5)
|
||||
response = await self._request(client, command, timeout=3.0, retries=2)
|
||||
# Response: DD E1 00 00 CHK_H CHK_L 77 (status byte 0x00 = OK)
|
||||
return response is not None and response[2] == 0x00
|
||||
finally:
|
||||
try:
|
||||
await client.disconnect()
|
||||
except Exception:
|
||||
pass
|
||||
self._buffer.clear()
|
||||
|
||||
@staticmethod
|
||||
def _build_mos_command(value: int) -> bytes:
|
||||
"""Build a MOS control write frame with correct checksum.
|
||||
|
||||
Frame: DD 5A E1 02 00 XX CHK_H CHK_L 77
|
||||
Checked bytes (per spec): command_code + length + data bytes
|
||||
= 0xE1 + 0x02 + 0x00 + XX
|
||||
Checksum = two's complement of sum, high byte first.
|
||||
|
||||
Verified against spec example:
|
||||
XX=0x02 → sum=0xE5 → ~0xE5+1=0xFF1B → CHK FF 1B ✓
|
||||
"""
|
||||
checked = [0xE1, 0x02, 0x00, value & 0xFF]
|
||||
checksum = (~sum(checked) + 1) & 0xFFFF
|
||||
return bytes([
|
||||
0xDD, 0x5A, 0xE1, 0x02, 0x00, value & 0xFF,
|
||||
(checksum >> 8) & 0xFF, checksum & 0xFF,
|
||||
0x77,
|
||||
])
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Frame parsers
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user