hitl_tester.modules.bms.smbus_types

Definitions for use in smbus.py

(c) 2020-2024 TurnAround Factor, Inc.

#

CUI DISTRIBUTION CONTROL

Controlled by: DLA J68 R&D SBIP

CUI Category: Small Business Research and Technology

Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS

POC: GOV SBIP Program Manager Denise Price, 571-767-0111

Distribution authorized to U.S. Government Agencies only, to protect information not owned by the

U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that

it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests

for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317,

Fort Belvoir, VA 22060-6221

#

SBIR DATA RIGHTS

Contract No.:SP4701-23-C-0083

Contractor Name: TurnAround Factor, Inc.

Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005

Expiration of SBIR Data Rights Period: September 24, 2029

The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer

software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights

in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause

contained in the above identified contract. No restrictions apply after the expiration date shown above. Any

reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce

the markings.

  1"""
  2Definitions for use in smbus.py
  3
  4# (c) 2020-2024 TurnAround Factor, Inc.
  5#
  6# CUI DISTRIBUTION CONTROL
  7# Controlled by: DLA J68 R&D SBIP
  8# CUI Category: Small Business Research and Technology
  9# Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS
 10# POC: GOV SBIP Program Manager Denise Price, 571-767-0111
 11# Distribution authorized to U.S. Government Agencies only, to protect information not owned by the
 12# U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that
 13# it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests
 14# for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317,
 15# Fort Belvoir, VA 22060-6221
 16#
 17# SBIR DATA RIGHTS
 18# Contract No.:SP4701-23-C-0083
 19# Contractor Name: TurnAround Factor, Inc.
 20# Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005
 21# Expiration of SBIR Data Rights Period: September 24, 2029
 22# The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer
 23# software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights
 24# in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause
 25# contained in the above identified contract. No restrictions apply after the expiration date shown above. Any
 26# reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce
 27# the markings.
 28"""
 29
 30from __future__ import annotations
 31
 32import datetime
 33import re
 34from dataclasses import dataclass
 35from enum import Enum, IntFlag, IntEnum
 36from functools import partial
 37from typing import Union
 38
 39from hitl_tester.modules.logger import logger
 40
 41
 42class StatusCodeAlarm(IntFlag):
 43    """Alarms that have been set."""
 44
 45    REMAINING_TIME_ALARM = 0x0100  # Average time to empty < remaining time alarm
 46    REMAINING_CAPACITY_ALARM = 0x0200  # Remaining capacity < remaining capacity alarm.
 47    TERMINATE_DISCHARGE_ALARM = 0x0800  # Battery is depleted, stop discharge
 48    OVER_TEMP_ALARM = 0x1000  # Temperature is above preset limit, stop charging
 49    TERMINATE_CHARGE_ALARM = 0x4000  # Temporarily suspend charging
 50    OVER_CHARGED_ALARM = 0x8000  # Battery is full, stop charging
 51
 52    def __str__(self) -> str:
 53        return " | ".join(str(flag.name).title() for flag in StatusCodeAlarm if self.value & flag)
 54
 55
 56class StatusCodeStatus(IntFlag):
 57    """Current status."""
 58
 59    FULLY_DISCHARGED = 0x0010  # Battery is completely discharged.
 60    FULLY_CHARGED = 0x0020  # Battery is full.
 61    DISCHARGING = 0x0040  # Discharge or no charge is occurring.
 62    INITIALIZED = 0x0080  # Cleared when calibration data set at factory has been lost. Data unreliable.
 63
 64    def __str__(self) -> str:
 65        return " | ".join(str(flag.name).title() for flag in StatusCodeStatus if self.value & flag)
 66
 67
 68class StatusCodeError(IntEnum):
 69    """Errors raised by the battery."""
 70
 71    OK = 0  # The Smart Battery processed the function code without detecting any errors.
 72    BUSY = 1  # The Smart Battery is unable to process the function code at this time.
 73    # The Smart Battery detected an attempt to read or write to a reserved function code. The Smart Battery detected an
 74    # attempt to access an unsupported optional manufacturer function code.
 75    RESERVED_COMMAND = 2
 76    UNSUPPORTED_COMMAND = 3  # The Smart Battery does not support this function code
 77    ACCESS_DENIED = 4  # The Smart Battery detected an attempt to write to a read only function code.
 78    UNDERFLOW_OVERFLOW = 5  # The Smart Battery detected a data overflow or under flow.
 79    BAD_SIZE = 6  # The Smart Battery detected an attempt to write to a function code with an incorrect size data block.
 80    UNKNOWN = 7  # The Smart Battery detected an unidentifiable error.
 81    MANUFACTURER_ERROR_1 = 8
 82    MANUFACTURER_ERROR_2 = 9
 83    MANUFACTURER_ERROR_3 = 10
 84    MANUFACTURER_ERROR_4 = 11
 85    MANUFACTURER_ERROR_5 = 12
 86    MANUFACTURER_ERROR_6 = 13
 87    MANUFACTURER_ERROR_7 = 14
 88    MANUFACTURER_ERROR_8 = 15
 89
 90    def __str__(self) -> str:
 91        return self.name.title()
 92
 93
 94class BatteryStatus:
 95    """Contains Alarm and Status bit flags."""
 96
 97    def __init__(self, raw_bytes: bytes):
 98        raw_int = int.from_bytes(raw_bytes, byteorder="little", signed=False)
 99        self.alarm = StatusCodeAlarm(raw_int & 0xFF00)
100        self.status = StatusCodeStatus(raw_int & 0x00F0)
101        self.error = StatusCodeError(raw_int & 0x000F)
102
103    def __str__(self):
104        return f"{self.alarm!s}; {self.status!s}; {self.error!s}"
105
106
107class BatteryMode:
108    """
109    Describes the various battery operational modes and reports the battery’s capabilities, modes, and flags minor
110    conditions requiring attention.
111    """
112
113    def __init__(self, raw_bytes: bytes):
114        raw_int = int.from_bytes(raw_bytes, byteorder="little", signed=False)
115
116        def bit(index: int) -> bool:
117            return bool(raw_int & (1 << index))
118
119        self.internal_charge_controller = bit(0)  # Internal Charge Controller is supported
120        self.primary_battery_support = bit(1)  # Primary or secondary battery is supported
121        self.condition_cycle_requested = bit(7)  # Conditioning Cycle Requested
122        self.charge_controller_enabled = bit(8)  # Internal Charge Control Enabled
123        self.primary_battery = bit(9)  # Battery operating in its primary role
124        self.alarm_mode = bit(13)  # Disable AlarmWarning broadcast to Host and Smart Battery Charger
125        self.charger_mode = bit(14)  # Disable broadcasts of ChargingVoltage/ChargingCurrent to Smart Battery Charger
126        self.capacity_mode = bit(15)  # Report in 10mW or 10mWh
127
128    def __str__(self):
129        return " | ".join(attribute.title() for attribute, value in self.__dict__.items() if value)
130
131
132class SpecInfoRevision(IntEnum):
133    """Specification revision (4-bit)."""
134
135    REVISION_11_OR_10 = 0x1  # Revision 1.0 or 1.1
136
137    def __str__(self) -> str:
138        text = self.name.replace("_", " ").capitalize()  # Modify underscore/case
139        return re.sub(r"(\d)(\d)", r"\1.\2", text)  # Place decimal in between digits
140
141
142class SpecInfoVersion(IntEnum):
143    """Specification revision (4-bit)."""
144
145    VERSION_10 = 0x1  # Version 1.0
146    VERSION_11 = 0x2  # Version 1.1
147    VERSION_11_PEC = 0x3  # Version 1.1 with optional PEC support
148
149    def __str__(self) -> str:
150        text = self.name.replace("_", " ").capitalize()  # Modify underscore/case
151        return re.sub(r"(\d)(\d)", r"\1.\2", text)  # Place decimal in between digits
152
153
154class SpecInfo:
155    """Information on which specification the battery is using."""
156
157    def __init__(self, raw_bytes: bytes):
158        data = int.from_bytes(raw_bytes, byteorder="little", signed=False)
159        self.revision = SpecInfoRevision(data & 0x000F)
160        self.version = SpecInfoVersion((data & 0x00F0) >> 4)
161        self.voltage_scale = 10 ** ((data & 0x0F00) >> 8)
162        self.current_scale = 10 ** ((data & 0xF000) >> 12)
163
164    def __str__(self):
165        return f"{self.revision!s}; {self.version!s}; {self.voltage_scale}; {self.current_scale}"
166
167
168def to_int(raw_bytes: bytes) -> int:
169    """Convert SMBus signed integer to integer."""
170    return int.from_bytes(raw_bytes, byteorder="little", signed=True)
171
172
173def to_unsinged_int(raw_bytes: bytes) -> int:
174    """Convert SMBus unsigned integer to integer."""
175    return int.from_bytes(raw_bytes, byteorder="little", signed=False)
176
177
178def to_bool(raw_bytes: bytes) -> bool:
179    """Convert SMBus bool to bool."""
180    return bool(int.from_bytes(raw_bytes, byteorder="little", signed=False))
181
182
183def to_string(raw_bytes: bytes) -> str:
184    """Convert SMBus string to string."""
185    ascii_string = raw_bytes.decode("ascii", errors="ignore")
186    return ascii_string.translate(dict.fromkeys(range(32)) | {44: None})  # Remove comma/control
187
188
189def to_hex(raw_bytes: bytes) -> str:
190    """Convert SMBus hex to hex string."""
191    return f"0x{raw_bytes[::-1].hex().upper()}"
192
193
194def to_date(raw_bytes: bytes) -> datetime.datetime:
195    """Convert SMBus date to datetime date."""
196    data = int.from_bytes(raw_bytes, byteorder="little", signed=False)
197    year = 1980 + (data >> 9)
198    month = (data & 0x01E0) >> 5
199    day = data & 0x001F
200    return datetime.datetime(year, month, day, tzinfo=datetime.timezone.utc)
201
202
203SMBusRegType = Union[int, bool, BatteryStatus, BatteryMode, SpecInfo, str, datetime.datetime]
204
205
206class SMBusFunctionType(Enum):
207    """How to interpret the bytes."""
208
209    BATTERY_STATUS = partial(BatteryStatus)
210    BATTERY_MODE = partial(BatteryMode)
211    SPEC_INFO = partial(SpecInfo)
212    INT = partial(to_int)
213    UNSIGNED_INT = partial(to_unsinged_int)
214    BOOL = partial(to_bool)
215    STRING = partial(to_string)
216    HEX = partial(to_hex)
217    DATE = partial(to_date)
218
219
220@dataclass
221class SMBusFunction:
222    """Describes how to access specific SMBus addresses."""
223
224    address: int  # Register address
225    size: int | None = 2  # Bytes returned on SMBus
226    type: SMBusFunctionType = SMBusFunctionType.UNSIGNED_INT  # How to decode the data
227    writable: bool = False  # Register can be written to
228    unit: str = ""  # Measurement unit
229
230
231class SMBusReg(Enum):
232    """Register definitions and their return types."""
233
234    MANUFACTURING_ACCESS = SMBusFunction(address=0x00, type=SMBusFunctionType.INT, writable=True, unit="${capacity}h")
235    REMAINING_CAPACITY_ALARM = SMBusFunction(address=0x01, type=SMBusFunctionType.INT, writable=True, unit="minute")
236    REMAINING_TIME_ALARM = SMBusFunction(address=0x02, writable=True)
237    BATTERY_MODE = SMBusFunction(address=0x03, type=SMBusFunctionType.BATTERY_MODE, writable=True)
238    AT_RATE = SMBusFunction(address=0x04, type=SMBusFunctionType.INT, writable=True, unit="$capacity")
239    AT_RATE_TIME_TO_FULL = SMBusFunction(address=0x05, unit="minute")
240    AT_RATE_TIME_TO_EMPTY = SMBusFunction(address=0x06, unit="minute")
241    AT_RATE_OK = SMBusFunction(address=0x07, type=SMBusFunctionType.BOOL)
242    TEMPERATURE = SMBusFunction(address=0x08, unit="dK")
243    VOLTAGE = SMBusFunction(address=0x09, unit="mV")
244    CURRENT = SMBusFunction(address=0x0A, type=SMBusFunctionType.INT, unit="mA")
245    AVERAGE_CURRENT = SMBusFunction(address=0x0B, type=SMBusFunctionType.INT, unit="mA")
246    MAX_ERROR = SMBusFunction(address=0x0C, size=2, unit="%")
247    RELATIVE_STATE_OF_CHARGE = SMBusFunction(address=0x0D, size=2, unit="%")
248    ABSOLUTE_STATE_OF_CHARGE = SMBusFunction(address=0x0E, size=2, unit="%")
249    REMAINING_CAPACITY = SMBusFunction(address=0x0F, writable=True, unit="${capacity}h")
250    FULL_CHARGE_CAPACITY = SMBusFunction(address=0x10, unit="${capacity}h")
251    RUN_TIME_TO_EMPTY = SMBusFunction(address=0x11, unit="minute")
252    AVERAGE_TIME_TO_EMPTY = SMBusFunction(address=0x12, unit="minute")
253    AVERAGE_TIME_TO_FULL = SMBusFunction(address=0x13, unit="minute")
254    CHARGING_CURRENT = SMBusFunction(address=0x14, unit="mA")
255    CHARGING_VOLTAGE = SMBusFunction(address=0x15, unit="mV")
256    BATTERY_STATUS = SMBusFunction(address=0x16, type=SMBusFunctionType.BATTERY_STATUS)
257    CYCLE_COUNT = SMBusFunction(address=0x17, writable=True)
258    DESIGN_CAPACITY = SMBusFunction(address=0x18, type=SMBusFunctionType.INT, writable=True, unit="${capacity}h")
259    DESIGN_VOLTAGE = SMBusFunction(address=0x19, type=SMBusFunctionType.INT, unit="mV")
260    SPECIFICATION_INFO = SMBusFunction(address=0x1A, type=SMBusFunctionType.SPEC_INFO)
261    MANUFACTURER_DATE = SMBusFunction(address=0x1B, type=SMBusFunctionType.DATE)
262    SERIAL_NUM = SMBusFunction(address=0x1C, type=SMBusFunctionType.HEX, writable=True)
263    MANUFACTURER_NAME = SMBusFunction(address=0x20, size=None, type=SMBusFunctionType.STRING)
264    DEVICE_NAME = SMBusFunction(address=0x21, size=None, type=SMBusFunctionType.STRING)
265    DEVICE_CHEMISTRY = SMBusFunction(address=0x22, size=None, type=SMBusFunctionType.STRING)
266    MANUFACTURER_DATA = SMBusFunction(address=0x23, size=None, type=SMBusFunctionType.STRING)
267    AUTHENTICATE = SMBusFunction(address=0x2F, size=None, type=SMBusFunctionType.STRING, writable=True)
268    CELL_VOLTAGE4 = SMBusFunction(address=0x3C, unit="mV")
269    CELL_VOLTAGE3 = SMBusFunction(address=0x3D, unit="mV")
270    CELL_VOLTAGE2 = SMBusFunction(address=0x3E, unit="mV")
271    CELL_VOLTAGE1 = SMBusFunction(address=0x3F, unit="mV")
272    AFE_DATA = SMBusFunction(address=0x45, size=None, type=SMBusFunctionType.STRING)
273    FET_CONTROL = SMBusFunction(address=0x46, type=SMBusFunctionType.HEX, writable=True)
274    STATE_OF_HEALTH = SMBusFunction(address=0x4F, type=SMBusFunctionType.HEX, unit="%")
275
276    @property
277    def fname(self) -> str:
278        """Format enum name as a string."""
279        return self.name.title().replace("_", " ")
280
281
282class BMSCommands(IntEnum):
283    """Supported BMS commands."""
284
285    FAULT_ENABLE = 0xEF
286    """Activate faults that are disable in low battery conditions."""
287
288    CALIBRATE = 0x0C
289    """Set the current calibration."""
290
291    ERASE_FLASH = 0xFD
292    """Erase flash memory (calibration, faults, cycle count, etc.)."""
293
294    PRODUCTION_MODE = 0x504D
295    """Once this is called, no going back. We can not erase flash or set calibration."""
296
297    ASSEMBLY_MODE = 0x414D
298    """This is needed to take us out of manufacturing mode, normally we never write to these flash registers again."""
299
300
301class SMBusError(Exception):
302    """Log the exception before raising it."""
303
304    def __init__(self, message):
305        """Output error message and raise exception."""
306        logger.write_critical_to_report(f"{type(self).__name__} - {message}")
307        super().__init__(message)
class StatusCodeAlarm(enum.IntFlag):
43class StatusCodeAlarm(IntFlag):
44    """Alarms that have been set."""
45
46    REMAINING_TIME_ALARM = 0x0100  # Average time to empty < remaining time alarm
47    REMAINING_CAPACITY_ALARM = 0x0200  # Remaining capacity < remaining capacity alarm.
48    TERMINATE_DISCHARGE_ALARM = 0x0800  # Battery is depleted, stop discharge
49    OVER_TEMP_ALARM = 0x1000  # Temperature is above preset limit, stop charging
50    TERMINATE_CHARGE_ALARM = 0x4000  # Temporarily suspend charging
51    OVER_CHARGED_ALARM = 0x8000  # Battery is full, stop charging
52
53    def __str__(self) -> str:
54        return " | ".join(str(flag.name).title() for flag in StatusCodeAlarm if self.value & flag)

Alarms that have been set.

REMAINING_TIME_ALARM = <StatusCodeAlarm.REMAINING_TIME_ALARM: 256>
REMAINING_CAPACITY_ALARM = <StatusCodeAlarm.REMAINING_CAPACITY_ALARM: 512>
TERMINATE_DISCHARGE_ALARM = <StatusCodeAlarm.TERMINATE_DISCHARGE_ALARM: 2048>
OVER_TEMP_ALARM = <StatusCodeAlarm.OVER_TEMP_ALARM: 4096>
TERMINATE_CHARGE_ALARM = <StatusCodeAlarm.TERMINATE_CHARGE_ALARM: 16384>
OVER_CHARGED_ALARM = <StatusCodeAlarm.OVER_CHARGED_ALARM: 32768>
Inherited Members
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
is_integer
real
imag
numerator
denominator
enum.Enum
name
value
class StatusCodeStatus(enum.IntFlag):
57class StatusCodeStatus(IntFlag):
58    """Current status."""
59
60    FULLY_DISCHARGED = 0x0010  # Battery is completely discharged.
61    FULLY_CHARGED = 0x0020  # Battery is full.
62    DISCHARGING = 0x0040  # Discharge or no charge is occurring.
63    INITIALIZED = 0x0080  # Cleared when calibration data set at factory has been lost. Data unreliable.
64
65    def __str__(self) -> str:
66        return " | ".join(str(flag.name).title() for flag in StatusCodeStatus if self.value & flag)

Current status.

FULLY_DISCHARGED = <StatusCodeStatus.FULLY_DISCHARGED: 16>
FULLY_CHARGED = <StatusCodeStatus.FULLY_CHARGED: 32>
DISCHARGING = <StatusCodeStatus.DISCHARGING: 64>
INITIALIZED = <StatusCodeStatus.INITIALIZED: 128>
Inherited Members
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
is_integer
real
imag
numerator
denominator
enum.Enum
name
value
class StatusCodeError(enum.IntEnum):
69class StatusCodeError(IntEnum):
70    """Errors raised by the battery."""
71
72    OK = 0  # The Smart Battery processed the function code without detecting any errors.
73    BUSY = 1  # The Smart Battery is unable to process the function code at this time.
74    # The Smart Battery detected an attempt to read or write to a reserved function code. The Smart Battery detected an
75    # attempt to access an unsupported optional manufacturer function code.
76    RESERVED_COMMAND = 2
77    UNSUPPORTED_COMMAND = 3  # The Smart Battery does not support this function code
78    ACCESS_DENIED = 4  # The Smart Battery detected an attempt to write to a read only function code.
79    UNDERFLOW_OVERFLOW = 5  # The Smart Battery detected a data overflow or under flow.
80    BAD_SIZE = 6  # The Smart Battery detected an attempt to write to a function code with an incorrect size data block.
81    UNKNOWN = 7  # The Smart Battery detected an unidentifiable error.
82    MANUFACTURER_ERROR_1 = 8
83    MANUFACTURER_ERROR_2 = 9
84    MANUFACTURER_ERROR_3 = 10
85    MANUFACTURER_ERROR_4 = 11
86    MANUFACTURER_ERROR_5 = 12
87    MANUFACTURER_ERROR_6 = 13
88    MANUFACTURER_ERROR_7 = 14
89    MANUFACTURER_ERROR_8 = 15
90
91    def __str__(self) -> str:
92        return self.name.title()

Errors raised by the battery.

OK = <StatusCodeError.OK: 0>
BUSY = <StatusCodeError.BUSY: 1>
RESERVED_COMMAND = <StatusCodeError.RESERVED_COMMAND: 2>
UNSUPPORTED_COMMAND = <StatusCodeError.UNSUPPORTED_COMMAND: 3>
ACCESS_DENIED = <StatusCodeError.ACCESS_DENIED: 4>
UNDERFLOW_OVERFLOW = <StatusCodeError.UNDERFLOW_OVERFLOW: 5>
BAD_SIZE = <StatusCodeError.BAD_SIZE: 6>
UNKNOWN = <StatusCodeError.UNKNOWN: 7>
MANUFACTURER_ERROR_1 = <StatusCodeError.MANUFACTURER_ERROR_1: 8>
MANUFACTURER_ERROR_2 = <StatusCodeError.MANUFACTURER_ERROR_2: 9>
MANUFACTURER_ERROR_3 = <StatusCodeError.MANUFACTURER_ERROR_3: 10>
MANUFACTURER_ERROR_4 = <StatusCodeError.MANUFACTURER_ERROR_4: 11>
MANUFACTURER_ERROR_5 = <StatusCodeError.MANUFACTURER_ERROR_5: 12>
MANUFACTURER_ERROR_6 = <StatusCodeError.MANUFACTURER_ERROR_6: 13>
MANUFACTURER_ERROR_7 = <StatusCodeError.MANUFACTURER_ERROR_7: 14>
MANUFACTURER_ERROR_8 = <StatusCodeError.MANUFACTURER_ERROR_8: 15>
Inherited Members
enum.Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
is_integer
real
imag
numerator
denominator
class BatteryStatus:
 95class BatteryStatus:
 96    """Contains Alarm and Status bit flags."""
 97
 98    def __init__(self, raw_bytes: bytes):
 99        raw_int = int.from_bytes(raw_bytes, byteorder="little", signed=False)
100        self.alarm = StatusCodeAlarm(raw_int & 0xFF00)
101        self.status = StatusCodeStatus(raw_int & 0x00F0)
102        self.error = StatusCodeError(raw_int & 0x000F)
103
104    def __str__(self):
105        return f"{self.alarm!s}; {self.status!s}; {self.error!s}"

Contains Alarm and Status bit flags.

BatteryStatus(raw_bytes: bytes)
 98    def __init__(self, raw_bytes: bytes):
 99        raw_int = int.from_bytes(raw_bytes, byteorder="little", signed=False)
100        self.alarm = StatusCodeAlarm(raw_int & 0xFF00)
101        self.status = StatusCodeStatus(raw_int & 0x00F0)
102        self.error = StatusCodeError(raw_int & 0x000F)
alarm
status
error
class BatteryMode:
108class BatteryMode:
109    """
110    Describes the various battery operational modes and reports the battery’s capabilities, modes, and flags minor
111    conditions requiring attention.
112    """
113
114    def __init__(self, raw_bytes: bytes):
115        raw_int = int.from_bytes(raw_bytes, byteorder="little", signed=False)
116
117        def bit(index: int) -> bool:
118            return bool(raw_int & (1 << index))
119
120        self.internal_charge_controller = bit(0)  # Internal Charge Controller is supported
121        self.primary_battery_support = bit(1)  # Primary or secondary battery is supported
122        self.condition_cycle_requested = bit(7)  # Conditioning Cycle Requested
123        self.charge_controller_enabled = bit(8)  # Internal Charge Control Enabled
124        self.primary_battery = bit(9)  # Battery operating in its primary role
125        self.alarm_mode = bit(13)  # Disable AlarmWarning broadcast to Host and Smart Battery Charger
126        self.charger_mode = bit(14)  # Disable broadcasts of ChargingVoltage/ChargingCurrent to Smart Battery Charger
127        self.capacity_mode = bit(15)  # Report in 10mW or 10mWh
128
129    def __str__(self):
130        return " | ".join(attribute.title() for attribute, value in self.__dict__.items() if value)

Describes the various battery operational modes and reports the battery’s capabilities, modes, and flags minor conditions requiring attention.

BatteryMode(raw_bytes: bytes)
114    def __init__(self, raw_bytes: bytes):
115        raw_int = int.from_bytes(raw_bytes, byteorder="little", signed=False)
116
117        def bit(index: int) -> bool:
118            return bool(raw_int & (1 << index))
119
120        self.internal_charge_controller = bit(0)  # Internal Charge Controller is supported
121        self.primary_battery_support = bit(1)  # Primary or secondary battery is supported
122        self.condition_cycle_requested = bit(7)  # Conditioning Cycle Requested
123        self.charge_controller_enabled = bit(8)  # Internal Charge Control Enabled
124        self.primary_battery = bit(9)  # Battery operating in its primary role
125        self.alarm_mode = bit(13)  # Disable AlarmWarning broadcast to Host and Smart Battery Charger
126        self.charger_mode = bit(14)  # Disable broadcasts of ChargingVoltage/ChargingCurrent to Smart Battery Charger
127        self.capacity_mode = bit(15)  # Report in 10mW or 10mWh
internal_charge_controller
primary_battery_support
condition_cycle_requested
charge_controller_enabled
primary_battery
alarm_mode
charger_mode
capacity_mode
class SpecInfoRevision(enum.IntEnum):
133class SpecInfoRevision(IntEnum):
134    """Specification revision (4-bit)."""
135
136    REVISION_11_OR_10 = 0x1  # Revision 1.0 or 1.1
137
138    def __str__(self) -> str:
139        text = self.name.replace("_", " ").capitalize()  # Modify underscore/case
140        return re.sub(r"(\d)(\d)", r"\1.\2", text)  # Place decimal in between digits

Specification revision (4-bit).

REVISION_11_OR_10 = <SpecInfoRevision.REVISION_11_OR_10: 1>
Inherited Members
enum.Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
is_integer
real
imag
numerator
denominator
class SpecInfoVersion(enum.IntEnum):
143class SpecInfoVersion(IntEnum):
144    """Specification revision (4-bit)."""
145
146    VERSION_10 = 0x1  # Version 1.0
147    VERSION_11 = 0x2  # Version 1.1
148    VERSION_11_PEC = 0x3  # Version 1.1 with optional PEC support
149
150    def __str__(self) -> str:
151        text = self.name.replace("_", " ").capitalize()  # Modify underscore/case
152        return re.sub(r"(\d)(\d)", r"\1.\2", text)  # Place decimal in between digits

Specification revision (4-bit).

VERSION_10 = <SpecInfoVersion.VERSION_10: 1>
VERSION_11 = <SpecInfoVersion.VERSION_11: 2>
VERSION_11_PEC = <SpecInfoVersion.VERSION_11_PEC: 3>
Inherited Members
enum.Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
is_integer
real
imag
numerator
denominator
class SpecInfo:
155class SpecInfo:
156    """Information on which specification the battery is using."""
157
158    def __init__(self, raw_bytes: bytes):
159        data = int.from_bytes(raw_bytes, byteorder="little", signed=False)
160        self.revision = SpecInfoRevision(data & 0x000F)
161        self.version = SpecInfoVersion((data & 0x00F0) >> 4)
162        self.voltage_scale = 10 ** ((data & 0x0F00) >> 8)
163        self.current_scale = 10 ** ((data & 0xF000) >> 12)
164
165    def __str__(self):
166        return f"{self.revision!s}; {self.version!s}; {self.voltage_scale}; {self.current_scale}"

Information on which specification the battery is using.

SpecInfo(raw_bytes: bytes)
158    def __init__(self, raw_bytes: bytes):
159        data = int.from_bytes(raw_bytes, byteorder="little", signed=False)
160        self.revision = SpecInfoRevision(data & 0x000F)
161        self.version = SpecInfoVersion((data & 0x00F0) >> 4)
162        self.voltage_scale = 10 ** ((data & 0x0F00) >> 8)
163        self.current_scale = 10 ** ((data & 0xF000) >> 12)
revision
version
voltage_scale
current_scale
def to_int(raw_bytes: bytes) -> int:
169def to_int(raw_bytes: bytes) -> int:
170    """Convert SMBus signed integer to integer."""
171    return int.from_bytes(raw_bytes, byteorder="little", signed=True)

Convert SMBus signed integer to integer.

def to_unsinged_int(raw_bytes: bytes) -> int:
174def to_unsinged_int(raw_bytes: bytes) -> int:
175    """Convert SMBus unsigned integer to integer."""
176    return int.from_bytes(raw_bytes, byteorder="little", signed=False)

Convert SMBus unsigned integer to integer.

def to_bool(raw_bytes: bytes) -> bool:
179def to_bool(raw_bytes: bytes) -> bool:
180    """Convert SMBus bool to bool."""
181    return bool(int.from_bytes(raw_bytes, byteorder="little", signed=False))

Convert SMBus bool to bool.

def to_string(raw_bytes: bytes) -> str:
184def to_string(raw_bytes: bytes) -> str:
185    """Convert SMBus string to string."""
186    ascii_string = raw_bytes.decode("ascii", errors="ignore")
187    return ascii_string.translate(dict.fromkeys(range(32)) | {44: None})  # Remove comma/control

Convert SMBus string to string.

def to_hex(raw_bytes: bytes) -> str:
190def to_hex(raw_bytes: bytes) -> str:
191    """Convert SMBus hex to hex string."""
192    return f"0x{raw_bytes[::-1].hex().upper()}"

Convert SMBus hex to hex string.

def to_date(raw_bytes: bytes) -> datetime.datetime:
195def to_date(raw_bytes: bytes) -> datetime.datetime:
196    """Convert SMBus date to datetime date."""
197    data = int.from_bytes(raw_bytes, byteorder="little", signed=False)
198    year = 1980 + (data >> 9)
199    month = (data & 0x01E0) >> 5
200    day = data & 0x001F
201    return datetime.datetime(year, month, day, tzinfo=datetime.timezone.utc)

Convert SMBus date to datetime date.

SMBusRegType = typing.Union[int, bool, BatteryStatus, BatteryMode, SpecInfo, str, datetime.datetime]
class SMBusFunctionType(enum.Enum):
207class SMBusFunctionType(Enum):
208    """How to interpret the bytes."""
209
210    BATTERY_STATUS = partial(BatteryStatus)
211    BATTERY_MODE = partial(BatteryMode)
212    SPEC_INFO = partial(SpecInfo)
213    INT = partial(to_int)
214    UNSIGNED_INT = partial(to_unsinged_int)
215    BOOL = partial(to_bool)
216    STRING = partial(to_string)
217    HEX = partial(to_hex)
218    DATE = partial(to_date)

How to interpret the bytes.

BATTERY_STATUS = <SMBusFunctionType.BATTERY_STATUS: functools.partial(<class 'BatteryStatus'>)>
BATTERY_MODE = <SMBusFunctionType.BATTERY_MODE: functools.partial(<class 'BatteryMode'>)>
SPEC_INFO = <SMBusFunctionType.SPEC_INFO: functools.partial(<class 'SpecInfo'>)>
INT = <SMBusFunctionType.INT: functools.partial(<function to_int>)>
UNSIGNED_INT = <SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>
BOOL = <SMBusFunctionType.BOOL: functools.partial(<function to_bool>)>
STRING = <SMBusFunctionType.STRING: functools.partial(<function to_string>)>
HEX = <SMBusFunctionType.HEX: functools.partial(<function to_hex>)>
DATE = <SMBusFunctionType.DATE: functools.partial(<function to_date>)>
Inherited Members
enum.Enum
name
value
@dataclass
class SMBusFunction:
221@dataclass
222class SMBusFunction:
223    """Describes how to access specific SMBus addresses."""
224
225    address: int  # Register address
226    size: int | None = 2  # Bytes returned on SMBus
227    type: SMBusFunctionType = SMBusFunctionType.UNSIGNED_INT  # How to decode the data
228    writable: bool = False  # Register can be written to
229    unit: str = ""  # Measurement unit

Describes how to access specific SMBus addresses.

SMBusFunction( address: int, size: int | None = 2, type: SMBusFunctionType = <SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable: bool = False, unit: str = '')
address: int
size: int | None = 2
type: SMBusFunctionType = <SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>
writable: bool = False
unit: str = ''
class SMBusReg(enum.Enum):
232class SMBusReg(Enum):
233    """Register definitions and their return types."""
234
235    MANUFACTURING_ACCESS = SMBusFunction(address=0x00, type=SMBusFunctionType.INT, writable=True, unit="${capacity}h")
236    REMAINING_CAPACITY_ALARM = SMBusFunction(address=0x01, type=SMBusFunctionType.INT, writable=True, unit="minute")
237    REMAINING_TIME_ALARM = SMBusFunction(address=0x02, writable=True)
238    BATTERY_MODE = SMBusFunction(address=0x03, type=SMBusFunctionType.BATTERY_MODE, writable=True)
239    AT_RATE = SMBusFunction(address=0x04, type=SMBusFunctionType.INT, writable=True, unit="$capacity")
240    AT_RATE_TIME_TO_FULL = SMBusFunction(address=0x05, unit="minute")
241    AT_RATE_TIME_TO_EMPTY = SMBusFunction(address=0x06, unit="minute")
242    AT_RATE_OK = SMBusFunction(address=0x07, type=SMBusFunctionType.BOOL)
243    TEMPERATURE = SMBusFunction(address=0x08, unit="dK")
244    VOLTAGE = SMBusFunction(address=0x09, unit="mV")
245    CURRENT = SMBusFunction(address=0x0A, type=SMBusFunctionType.INT, unit="mA")
246    AVERAGE_CURRENT = SMBusFunction(address=0x0B, type=SMBusFunctionType.INT, unit="mA")
247    MAX_ERROR = SMBusFunction(address=0x0C, size=2, unit="%")
248    RELATIVE_STATE_OF_CHARGE = SMBusFunction(address=0x0D, size=2, unit="%")
249    ABSOLUTE_STATE_OF_CHARGE = SMBusFunction(address=0x0E, size=2, unit="%")
250    REMAINING_CAPACITY = SMBusFunction(address=0x0F, writable=True, unit="${capacity}h")
251    FULL_CHARGE_CAPACITY = SMBusFunction(address=0x10, unit="${capacity}h")
252    RUN_TIME_TO_EMPTY = SMBusFunction(address=0x11, unit="minute")
253    AVERAGE_TIME_TO_EMPTY = SMBusFunction(address=0x12, unit="minute")
254    AVERAGE_TIME_TO_FULL = SMBusFunction(address=0x13, unit="minute")
255    CHARGING_CURRENT = SMBusFunction(address=0x14, unit="mA")
256    CHARGING_VOLTAGE = SMBusFunction(address=0x15, unit="mV")
257    BATTERY_STATUS = SMBusFunction(address=0x16, type=SMBusFunctionType.BATTERY_STATUS)
258    CYCLE_COUNT = SMBusFunction(address=0x17, writable=True)
259    DESIGN_CAPACITY = SMBusFunction(address=0x18, type=SMBusFunctionType.INT, writable=True, unit="${capacity}h")
260    DESIGN_VOLTAGE = SMBusFunction(address=0x19, type=SMBusFunctionType.INT, unit="mV")
261    SPECIFICATION_INFO = SMBusFunction(address=0x1A, type=SMBusFunctionType.SPEC_INFO)
262    MANUFACTURER_DATE = SMBusFunction(address=0x1B, type=SMBusFunctionType.DATE)
263    SERIAL_NUM = SMBusFunction(address=0x1C, type=SMBusFunctionType.HEX, writable=True)
264    MANUFACTURER_NAME = SMBusFunction(address=0x20, size=None, type=SMBusFunctionType.STRING)
265    DEVICE_NAME = SMBusFunction(address=0x21, size=None, type=SMBusFunctionType.STRING)
266    DEVICE_CHEMISTRY = SMBusFunction(address=0x22, size=None, type=SMBusFunctionType.STRING)
267    MANUFACTURER_DATA = SMBusFunction(address=0x23, size=None, type=SMBusFunctionType.STRING)
268    AUTHENTICATE = SMBusFunction(address=0x2F, size=None, type=SMBusFunctionType.STRING, writable=True)
269    CELL_VOLTAGE4 = SMBusFunction(address=0x3C, unit="mV")
270    CELL_VOLTAGE3 = SMBusFunction(address=0x3D, unit="mV")
271    CELL_VOLTAGE2 = SMBusFunction(address=0x3E, unit="mV")
272    CELL_VOLTAGE1 = SMBusFunction(address=0x3F, unit="mV")
273    AFE_DATA = SMBusFunction(address=0x45, size=None, type=SMBusFunctionType.STRING)
274    FET_CONTROL = SMBusFunction(address=0x46, type=SMBusFunctionType.HEX, writable=True)
275    STATE_OF_HEALTH = SMBusFunction(address=0x4F, type=SMBusFunctionType.HEX, unit="%")
276
277    @property
278    def fname(self) -> str:
279        """Format enum name as a string."""
280        return self.name.title().replace("_", " ")

Register definitions and their return types.

MANUFACTURING_ACCESS = <SMBusReg.MANUFACTURING_ACCESS: SMBusFunction(address=0, size=2, type=<SMBusFunctionType.INT: functools.partial(<function to_int>)>, writable=True, unit='${capacity}h')>
REMAINING_CAPACITY_ALARM = <SMBusReg.REMAINING_CAPACITY_ALARM: SMBusFunction(address=1, size=2, type=<SMBusFunctionType.INT: functools.partial(<function to_int>)>, writable=True, unit='minute')>
REMAINING_TIME_ALARM = <SMBusReg.REMAINING_TIME_ALARM: SMBusFunction(address=2, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=True, unit='')>
BATTERY_MODE = <SMBusReg.BATTERY_MODE: SMBusFunction(address=3, size=2, type=<SMBusFunctionType.BATTERY_MODE: functools.partial(<class 'BatteryMode'>)>, writable=True, unit='')>
AT_RATE = <SMBusReg.AT_RATE: SMBusFunction(address=4, size=2, type=<SMBusFunctionType.INT: functools.partial(<function to_int>)>, writable=True, unit='$capacity')>
AT_RATE_TIME_TO_FULL = <SMBusReg.AT_RATE_TIME_TO_FULL: SMBusFunction(address=5, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='minute')>
AT_RATE_TIME_TO_EMPTY = <SMBusReg.AT_RATE_TIME_TO_EMPTY: SMBusFunction(address=6, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='minute')>
AT_RATE_OK = <SMBusReg.AT_RATE_OK: SMBusFunction(address=7, size=2, type=<SMBusFunctionType.BOOL: functools.partial(<function to_bool>)>, writable=False, unit='')>
TEMPERATURE = <SMBusReg.TEMPERATURE: SMBusFunction(address=8, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='dK')>
VOLTAGE = <SMBusReg.VOLTAGE: SMBusFunction(address=9, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='mV')>
CURRENT = <SMBusReg.CURRENT: SMBusFunction(address=10, size=2, type=<SMBusFunctionType.INT: functools.partial(<function to_int>)>, writable=False, unit='mA')>
AVERAGE_CURRENT = <SMBusReg.AVERAGE_CURRENT: SMBusFunction(address=11, size=2, type=<SMBusFunctionType.INT: functools.partial(<function to_int>)>, writable=False, unit='mA')>
MAX_ERROR = <SMBusReg.MAX_ERROR: SMBusFunction(address=12, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='%')>
RELATIVE_STATE_OF_CHARGE = <SMBusReg.RELATIVE_STATE_OF_CHARGE: SMBusFunction(address=13, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='%')>
ABSOLUTE_STATE_OF_CHARGE = <SMBusReg.ABSOLUTE_STATE_OF_CHARGE: SMBusFunction(address=14, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='%')>
REMAINING_CAPACITY = <SMBusReg.REMAINING_CAPACITY: SMBusFunction(address=15, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=True, unit='${capacity}h')>
FULL_CHARGE_CAPACITY = <SMBusReg.FULL_CHARGE_CAPACITY: SMBusFunction(address=16, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='${capacity}h')>
RUN_TIME_TO_EMPTY = <SMBusReg.RUN_TIME_TO_EMPTY: SMBusFunction(address=17, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='minute')>
AVERAGE_TIME_TO_EMPTY = <SMBusReg.AVERAGE_TIME_TO_EMPTY: SMBusFunction(address=18, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='minute')>
AVERAGE_TIME_TO_FULL = <SMBusReg.AVERAGE_TIME_TO_FULL: SMBusFunction(address=19, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='minute')>
CHARGING_CURRENT = <SMBusReg.CHARGING_CURRENT: SMBusFunction(address=20, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='mA')>
CHARGING_VOLTAGE = <SMBusReg.CHARGING_VOLTAGE: SMBusFunction(address=21, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='mV')>
BATTERY_STATUS = <SMBusReg.BATTERY_STATUS: SMBusFunction(address=22, size=2, type=<SMBusFunctionType.BATTERY_STATUS: functools.partial(<class 'BatteryStatus'>)>, writable=False, unit='')>
CYCLE_COUNT = <SMBusReg.CYCLE_COUNT: SMBusFunction(address=23, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=True, unit='')>
DESIGN_CAPACITY = <SMBusReg.DESIGN_CAPACITY: SMBusFunction(address=24, size=2, type=<SMBusFunctionType.INT: functools.partial(<function to_int>)>, writable=True, unit='${capacity}h')>
DESIGN_VOLTAGE = <SMBusReg.DESIGN_VOLTAGE: SMBusFunction(address=25, size=2, type=<SMBusFunctionType.INT: functools.partial(<function to_int>)>, writable=False, unit='mV')>
SPECIFICATION_INFO = <SMBusReg.SPECIFICATION_INFO: SMBusFunction(address=26, size=2, type=<SMBusFunctionType.SPEC_INFO: functools.partial(<class 'SpecInfo'>)>, writable=False, unit='')>
MANUFACTURER_DATE = <SMBusReg.MANUFACTURER_DATE: SMBusFunction(address=27, size=2, type=<SMBusFunctionType.DATE: functools.partial(<function to_date>)>, writable=False, unit='')>
SERIAL_NUM = <SMBusReg.SERIAL_NUM: SMBusFunction(address=28, size=2, type=<SMBusFunctionType.HEX: functools.partial(<function to_hex>)>, writable=True, unit='')>
MANUFACTURER_NAME = <SMBusReg.MANUFACTURER_NAME: SMBusFunction(address=32, size=None, type=<SMBusFunctionType.STRING: functools.partial(<function to_string>)>, writable=False, unit='')>
DEVICE_NAME = <SMBusReg.DEVICE_NAME: SMBusFunction(address=33, size=None, type=<SMBusFunctionType.STRING: functools.partial(<function to_string>)>, writable=False, unit='')>
DEVICE_CHEMISTRY = <SMBusReg.DEVICE_CHEMISTRY: SMBusFunction(address=34, size=None, type=<SMBusFunctionType.STRING: functools.partial(<function to_string>)>, writable=False, unit='')>
MANUFACTURER_DATA = <SMBusReg.MANUFACTURER_DATA: SMBusFunction(address=35, size=None, type=<SMBusFunctionType.STRING: functools.partial(<function to_string>)>, writable=False, unit='')>
AUTHENTICATE = <SMBusReg.AUTHENTICATE: SMBusFunction(address=47, size=None, type=<SMBusFunctionType.STRING: functools.partial(<function to_string>)>, writable=True, unit='')>
CELL_VOLTAGE4 = <SMBusReg.CELL_VOLTAGE4: SMBusFunction(address=60, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='mV')>
CELL_VOLTAGE3 = <SMBusReg.CELL_VOLTAGE3: SMBusFunction(address=61, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='mV')>
CELL_VOLTAGE2 = <SMBusReg.CELL_VOLTAGE2: SMBusFunction(address=62, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='mV')>
CELL_VOLTAGE1 = <SMBusReg.CELL_VOLTAGE1: SMBusFunction(address=63, size=2, type=<SMBusFunctionType.UNSIGNED_INT: functools.partial(<function to_unsinged_int>)>, writable=False, unit='mV')>
AFE_DATA = <SMBusReg.AFE_DATA: SMBusFunction(address=69, size=None, type=<SMBusFunctionType.STRING: functools.partial(<function to_string>)>, writable=False, unit='')>
FET_CONTROL = <SMBusReg.FET_CONTROL: SMBusFunction(address=70, size=2, type=<SMBusFunctionType.HEX: functools.partial(<function to_hex>)>, writable=True, unit='')>
STATE_OF_HEALTH = <SMBusReg.STATE_OF_HEALTH: SMBusFunction(address=79, size=2, type=<SMBusFunctionType.HEX: functools.partial(<function to_hex>)>, writable=False, unit='%')>
fname: str
277    @property
278    def fname(self) -> str:
279        """Format enum name as a string."""
280        return self.name.title().replace("_", " ")

Format enum name as a string.

Inherited Members
enum.Enum
name
value
class BMSCommands(enum.IntEnum):
283class BMSCommands(IntEnum):
284    """Supported BMS commands."""
285
286    FAULT_ENABLE = 0xEF
287    """Activate faults that are disable in low battery conditions."""
288
289    CALIBRATE = 0x0C
290    """Set the current calibration."""
291
292    ERASE_FLASH = 0xFD
293    """Erase flash memory (calibration, faults, cycle count, etc.)."""
294
295    PRODUCTION_MODE = 0x504D
296    """Once this is called, no going back. We can not erase flash or set calibration."""
297
298    ASSEMBLY_MODE = 0x414D
299    """This is needed to take us out of manufacturing mode, normally we never write to these flash registers again."""

Supported BMS commands.

FAULT_ENABLE = <BMSCommands.FAULT_ENABLE: 239>

Activate faults that are disable in low battery conditions.

CALIBRATE = <BMSCommands.CALIBRATE: 12>

Set the current calibration.

ERASE_FLASH = <BMSCommands.ERASE_FLASH: 253>

Erase flash memory (calibration, faults, cycle count, etc.).

PRODUCTION_MODE = <BMSCommands.PRODUCTION_MODE: 20557>

Once this is called, no going back. We can not erase flash or set calibration.

ASSEMBLY_MODE = <BMSCommands.ASSEMBLY_MODE: 16717>

This is needed to take us out of manufacturing mode, normally we never write to these flash registers again.

Inherited Members
enum.Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
is_integer
real
imag
numerator
denominator
class SMBusError(builtins.Exception):
302class SMBusError(Exception):
303    """Log the exception before raising it."""
304
305    def __init__(self, message):
306        """Output error message and raise exception."""
307        logger.write_critical_to_report(f"{type(self).__name__} - {message}")
308        super().__init__(message)

Log the exception before raising it.

SMBusError(message)
305    def __init__(self, message):
306        """Output error message and raise exception."""
307        logger.write_critical_to_report(f"{type(self).__name__} - {message}")
308        super().__init__(message)

Output error message and raise exception.

Inherited Members
builtins.BaseException
with_traceback
add_note
args