Files
ha-xiaoxiang-bms/custom_components/xiaoxiang_bms/select.py
T
Jannis b52b25973e 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>
2026-04-11 20:46:10 +02:00

74 lines
2.4 KiB
Python

"""Select entity for Xiaoxiang Smart BMS MOS gate control."""
from __future__ import annotations
from homeassistant.components.select import SelectEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
DOMAIN,
MOS_BOTH_OFF,
MOS_CHARGE_OFF,
MOS_DISCHARGE_OFF,
MOS_NORMAL,
)
from .coordinator import BmsCoordinator
# Maps the human-readable option to the XX byte value in the write command
_OPTION_TO_VALUE: dict[str, int] = {
"Normal": MOS_NORMAL,
"Charge Disabled": MOS_CHARGE_OFF,
"Discharge Disabled": MOS_DISCHARGE_OFF,
"Both Disabled": MOS_BOTH_OFF,
}
_OPTIONS = list(_OPTION_TO_VALUE)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
coordinator: BmsCoordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities([BmsMosSelect(coordinator)])
class BmsMosSelect(CoordinatorEntity[BmsCoordinator], SelectEntity):
"""Dropdown to control the BMS charge/discharge MOSFET gates.
Current state is derived from the MOS status bits returned by the BMS
on every poll. Selecting an option sends the write command (0xE1)
immediately and triggers a coordinator refresh so sensors update.
"""
_attr_has_entity_name = True
_attr_name = "MOS Control"
_attr_options = _OPTIONS
_attr_icon = "mdi:electric-switch"
def __init__(self, coordinator: BmsCoordinator) -> None:
super().__init__(coordinator)
self._attr_unique_id = f"{coordinator.address}_mos_control"
self._attr_device_info = coordinator.device_info
@property
def current_option(self) -> str | None:
data = self.coordinator.data
if not data:
return None
charge = data.get("mos_charge_enabled", True)
discharge = data.get("mos_discharge_enabled", True)
if charge and discharge:
return "Normal"
if not charge and discharge:
return "Charge Disabled"
if charge and not discharge:
return "Discharge Disabled"
return "Both Disabled"
async def async_select_option(self, option: str) -> None:
value = _OPTION_TO_VALUE[option]
await self.coordinator.async_write_mos(value)