class CompletionCodeError(Exception):
    """IPMI completion code not OK."""

    def __init__(self, cc, cmdid=None):
        self.cc = cc
        self.cc_desc = self.find_cc_desc(cc)

    def __str__(self):
        return "%s cc=0x%02x desc=%s" \
            % (self.__class__.__name__, self.cc, self.cc_desc)

    @staticmethod
    def find_cc_desc(error_cc):
        for cc in COMPLETION_CODE_DESCR:
            if error_cc == cc[0]:
                return cc[1]
        return "Unknown error description" class DecodingError(Exception):
    """Error on message decoding."""
    pass def check_completion_code(cc):
    if cc != constants.CC_OK:
        raise CompletionCodeError(cc) class ByteBuffer(object):
    def __init__(self, data=None):

        if data is not None:
            self.array = array('B', data)
        else:
            self.array = array('B')

    def push_unsigned_int(self, value, length):
        for i in range(length):
            self.array.append((value >> (8*i) & 0xff))

    def pop_unsigned_int(self, length):
        value = 0
        for i in range(length):
            try:
                value |= self.array.pop(0) << (8*i)
            except IndexError:
                raise DecodingError('Data too short for message')
        return value

    def push_string(self, value):
        if _PY3 and isinstance(value, str):
            # Encode Unicode to UTF-8
            value = value.encode()
        py3_array_frombytes(self.array, value)

    def pop_string(self, length):
        string = self.array[0:length]
        del self.array[0:length]
        return py3_array_tobytes(string)
        # return py3dec_unic_bytes_fix(string.tostring())

    def pop_slice(self, length):
        if len(self.array) < length:
            raise DecodingError('Data too short for message')

        c = ByteBuffer(self.array[0:length])
        self.__delslice__(0, length)
        return c

    def tobytes(self):
        return self.array.tobytes()

    def tostring(self):
        return py3_array_tobytes(self.array)

    def extend(self, data):
        self.array.extend(data)

    def append_array(self, a):
        self.array.extend(a)

    def __getslice__(self, a, b):
        return self.array[a:b]

    def __delslice__(self, a, b):
        del self.array[a:b]

    def __len__(self):
        return len(self.array)

    def __getitem__(self, idx):
        return self.array[idx] def create_request_by_name(self, name, *args, **kwargs):
    return self.registry[name + "Req"](*args, **kwargs) CC_OK = 0x00
CC_NODE_BUSY = 0xc0
CC_INV_CMD = 0xc1
CC_INV_CMD_FOR_LUN = 0xc2
CC_TIMEOUT = 0xc3
CC_OUT_OF_SPACE = 0xc4
CC_RES_CANCELED = 0xc5
CC_REQ_DATA_TRUNC = 0xc6
CC_REQ_DATA_INV_LENGTH = 0xc7
CC_REQ_DATA_FIELD_EXCEED = 0xc8
CC_PARAM_OUT_OF_RANGE = 0xc9
CC_CANT_RET_NUM_REQ_BYTES = 0xca
CC_REQ_DATA_NOT_PRESENT = 0xcb
CC_INV_DATA_FIELD_IN_REQ = 0xcc
CC_ILL_SENSOR_OR_RECORD = 0xcd
CC_RESP_COULD_NOT_BE_PRV = 0xce
CC_CANT_RESP_DUPLI_REQ = 0xcf
CC_CANT_RESP_SDRR_UPDATE = 0xd0
CC_CANT_RESP_FIRM_UPDATE = 0xd1
CC_CANT_RESP_BMC_INIT = 0xd2
CC_DESTINATION_UNAVAILABLE = 0xd3
CC_INSUFFICIENT_PRIVILEGES = 0xd4
CC_NOT_SUPPORTED_PRESENT_STATE = 0xd5
CC_ILLEGAL_COMMAND_DISABLED = 0xd6
CC_UNSPECIFIED_ERROR = 0xff
COMPLETION_CODE_DESCR = (
    (CC_OK, 'Command Completed Normally'),
    (CC_NODE_BUSY, 'Node Busy'),
    (CC_INV_CMD, 'Invalid Command'),
    (CC_INV_CMD_FOR_LUN, 'Command invalid for given LUN'),
    (CC_TIMEOUT, 'Timeout while processing command'),
    (CC_OUT_OF_SPACE, 'Out of space'),
    (CC_RES_CANCELED, 'Reservation Canceled or Invalid Reservation ID'),
    (CC_REQ_DATA_TRUNC, 'Request data truncated'),
    (CC_REQ_DATA_INV_LENGTH, 'Request data length invalid'),
    (CC_REQ_DATA_FIELD_EXCEED, 'Request data field length limit exceeded'),
    (CC_PARAM_OUT_OF_RANGE, 'Parameter out of range'),
    (CC_CANT_RET_NUM_REQ_BYTES,
     'Cannot return number of requested data bytes'),
    (CC_REQ_DATA_NOT_PRESENT, 'Requested data not present'),
    (CC_INV_DATA_FIELD_IN_REQ, 'Invalid data field in Request'),
    (CC_ILL_SENSOR_OR_RECORD,
     'Command illegal for specified sensor or record'),
    (CC_RESP_COULD_NOT_BE_PRV, 'Command response could not be provided'),
    (CC_CANT_RESP_DUPLI_REQ, 'Cannot execute duplicated request'),
    (CC_CANT_RESP_SDRR_UPDATE,
     'Command response could not be provided. SDRR in update mode'),
    (CC_CANT_RESP_FIRM_UPDATE,
     'Command response could not be provided. Device in firmware '
     'update mode'),
    (CC_CANT_RESP_BMC_INIT,
     'Command response could not be provided. BMC initialization or '
     'initialization agent in progress'),
    (CC_DESTINATION_UNAVAILABLE, 'Destination unavailable'),
    (CC_INSUFFICIENT_PRIVILEGES,
     'Cannot execute command due to insufficient privilege level'),
    (CC_NOT_SUPPORTED_PRESENT_STATE,
     'Cannot execute command. Not supported in present state'),
    (CC_ILLEGAL_COMMAND_DISABLED,
     'Cannot execute command. Command sub-function has been disabled '
     'or is unavailable'),
    (CC_UNSPECIFIED_ERROR, 'Unspecified error'),
)
NETFN_CHASSIS = 0x00
NETFN_BRIDGE = 0x02
NETFN_SENSOR_EVENT = 0x04
NETFN_APP = 0x06
NETFN_FIRMWARE = 0x08
NETFN_STORAGE = 0x0a
NETFN_TRANSPORT = 0x0c
NETFN_GROUP_EXTENSION = 0x2c
NETFN_OEM = 0x2e
NETFN_DESCR = (
    (NETFN_CHASSIS, 'Chassis'),
    (NETFN_BRIDGE, 'Bridge'),
    (NETFN_SENSOR_EVENT, 'Sensor/Event'),
    (NETFN_APP, 'App'),
    (NETFN_FIRMWARE, 'Firmware'),
    (NETFN_STORAGE, 'Storage'),
    (NETFN_TRANSPORT, 'Transport'),
    (NETFN_GROUP_EXTENSION, 'Group Extension'),
    (NETFN_OEM, 'IANA'),
)
CMDID_GET_DEVICE_ID = 0x01
CMDID_COLD_RESET = 0x02
CMDID_WARM_RESET = 0x03
CMDID_GET_SELF_TEST_RESULTS = 0x04
CMDID_MANUFACTURING_TEST_ON = 0x05
CMDID_SET_ACPI_POWER_STATE = 0x06
CMDID_GET_ACPI_POWER_STATE = 0x07
CMDID_GET_DEVICE_GUID = 0x08
CMDID_GET_NETFN_SUPPORT = 0x09
CMDID_GET_COMMAND_SUPPORT = 0x0a
CMDID_GET_COMMAND_SUBFUNCTION_SUPPORT = 0x0b
CMDID_GET_CONFIGURATION_COMMANDS = 0x0c
CMDID_GET_CONFIGURATION_SUBFUNCTION_COMMANDS = 0x0d
CMDID_SET_COMMAND_ENABLES = 0x60
CMDID_GET_COMMAND_ENABLES = 0x61
CMDID_SET_COMMAND_SUBFUNCTION_ENABLES = 0x62
CMDID_GET_COMMAND_SUBFUNCTION_ENABLES = 0x63
CMDID_GET_OEM_NETFN_IANA_SUPPORT = 0x64
CMDID_RESET_WATCHDOG_TIMER = 0x22
CMDID_SET_WATCHDOG_TIMER = 0x24
CMDID_GET_WATCHDOG_TIMER = 0x25
CMDID_SET_BMC_GLOBAL_ENABLES = 0x2e
CMDID_GET_BMC_GLOBAL_ENABLES = 0x2f
CMDID_CLEAR_MESSAGE_FLAGS = 0x30
CMDID_GET_MESSAGE_FLAGS = 0x31
CMDID_ENABLE_MESSAGE_CHANNEL_RECEIVE = 0x32
CMDID_GET_MESSAGE = 0x33
CMDID_SEND_MESSAGE = 0x34
CMDID_READ_EVENT_MESSAGE_BUFFER = 0x35
CMDID_GET_BT_INTERFACE_CAPABILITIES = 0x36
CMDID_GET_SYSTEM_GUID = 0x37
CMDID_GET_CHANNEL_AUTHENTICATION_CAPABILITIES = 0x38
CMDID_GET_SESSION_CHALLENGE = 0x39
CMDID_ACTIVATE_SESSION = 0x3a
CMDID_SET_SESSION_PRIVILEGE_LEVEL = 0x3b
CMDID_CLOSE_SESSION = 0x3c
CMDID_GET_SESSION_INFO = 0x3d
CMDID_GET_AUTHCODE = 0x3f
CMDID_SET_CHANNEL_ACCESS = 0x40
CMDID_GET_CHANNEL_ACCESS = 0x41
CMDID_GET_CHANNEL_INFO = 0x42
CMDID_SET_USER_ACCESS = 0x43
CMDID_GET_USER_ACCESS = 0x44
CMDID_SET_USER_NAME = 0x45
CMDID_GET_USER_NAME = 0x46
CMDID_SET_USER_PASSWORD = 0x47
CMDID_ACTIVATE_PAYLOAD = 0x48
CMDID_DEACTIVATE_PAYLOAD = 0x49
CMDID_GET_PAYLOAD_ACTITVATION_STATUS = 0x4a
CMDID_GET_PAYLOAD_INSTANCE_INFO = 0x4b
CMDID_SET_USER_PAYLOAD_ACCESS = 0x4c
CMDID_GET_USER_PAYLOAD_ACCESS = 0x4d
CMDID_GET_CHANNEL_PAYLOAD_SUPPORT = 0x4e
CMDID_GET_CHANNEL_PAYLOAD_VERSION = 0x4f
CMDID_GET_CHANNEL_OEM_PAYLOAD_INFO = 0x50
CMDID_MASTER_WRITE_READ = 0x52
CMDID_GET_CHANNEL_CIPHER_SUITES = 0x54
CMDID_SUSPEND_RESUME_PAYLOAD_ENCRYPTION = 0x55
CMDID_SET_CHANNEL_SECURITY_KEYS = 0x56
CMDID_GET_SYSTEM_INTERFACE_CAPABILITIES = 0x57
CMDID_SET_SYSTEM_INFO_PARAMETERS = 0x58
CMDID_GET_SYSTEM_INFO_PARAMETERS = 0x59
CMDID_GET_CHASSIS_CAPABILITIES = 0x00
CMDID_GET_CHASSIS_STATUS = 0x01
CMDID_CHASSIS_CONTROL = 0x02
CMDID_CHASSIS_RESET = 0x03
CMDID_CHASSIS_IDENTIFY = 0x04
CMDID_SET_CHASSIS_CAPABILITIES = 0x05
CMDID_SET_POWER_RESTORE_POLICY = 0x06
CMDID_GET_SYSTEM_RESTART_CAUSE = 0x07
CMDID_SET_SYSTEM_BOOT_OPTIONS = 0x08
CMDID_GET_SYSTEM_BOOT_OPTIONS = 0x09
CMDID_GET_POH_COUNTER = 0x0f
CMDID_SET_EVENT_RECEIVER = 0x00
CMDID_GET_EVENT_RECEIVER = 0x01
CMDID_PLATFORM_EVENT = 0x02
CMDID_GET_PEF_CAPABILITIES = 0x10
CMDID_ARM_PEF_POSTPONE_TIMER = 0x11
CMDID_SET_PEFCONFIGURATION_PARAMETERS = 0x12
CMDID_GET_PEFCONFIGURATION_PARAMETERS = 0x13
CMDID_SET_LAST_PROCESSED_EVENT_ID = 0x14
CMDID_GET_LAST_PROCESSED_EVENT_ID = 0x15
CMDID_ALERT_IMMEDIATE = 0x16
CMDID_PET_ACKNOWLEDGE = 0x17
CMDID_GET_DEVICE_SDR_INFO = 0x20
CMDID_GET_DEVICE_SDR = 0x21
CMDID_RESERVE_DEVICE_SDR_REPOSITORY = 0x22
CMDID_GET_SENSOR_READING_FACTOR = 0x23
CMDID_SET_SENSOR_HYSTERESIS = 0x24
CMDID_GET_SENSOR_HYSTERESIS = 0x25
CMDID_SET_SENSOR_THRESHOLD = 0x26
CMDID_GET_SENSOR_THRESHOLD = 0x27
CMDID_SET_SENSOR_EVENT_ENABLE = 0x28
CMDID_GET_SENSOR_EVENT_ENABLE = 0x29
CMDID_RE_ARM_SENSOR = 0x2a
CMDID_GET_SENSOR_EVENT_STATUS = 0x2b
CMDID_GET_SENSOR_READING = 0x2d
CMDID_SET_SENSOR_TYPE = 0x2e
CMDID_GET_SENSOR_TYPE = 0x2f
CMDID_SET_SENSOR_READING_AND_EVENT_STATUS = 0x30
CMDID_GET_FRU_INVENTORY_AREA_INFO = 0x10
CMDID_READ_FRU_DATA = 0x11
CMDID_WRITE_FRU_DATA = 0x12
CMDID_GET_SDR_REPOSITORY_INFO = 0x20
CMDID_GET_SDR_REPOSITORY_ALLOCATION_INFO = 0x21
CMDID_RESERVE_SDR_REPOSITORY = 0x22
CMDID_GET_SDR = 0x23
CMDID_ADD_SDR = 0x24
CMDID_PARTIAL_ADD_SDR = 0x25
CMDID_DELETE_SDR = 0x26
CMDID_CLEAR_SDR_REPOSITORY = 0x27
CMDID_GET_SDR_REPOSITORY_TIME = 0x28
CMDID_SET_SDR_REPOSITORY_TIME = 0x29
CMDID_ENTER_SDR_REPOSITORY_UPDATE_MODE = 0x2a
CMDID_EXIT_SDR_REPOSITORY_UPDATE_MODE = 0x2b
CMDID_RUN_INITIALIZATION_AGENT = 0x2c
CMDID_GET_SEL_INFO = 0x40
CMDID_GET_SEL_ALLOCATION_INFO = 0x41
CMDID_RESERVE_SEL = 0x42
CMDID_GET_SEL_ENTRY = 0x43
CMDID_ADD_SEL_ENTRY = 0x44
CMDID_PARTIAL_ADD_SEL_ENTRY = 0x45
CMDID_DELETE_SEL_ENTRY = 0x46
CMDID_CLEAR_SEL = 0x47
CMDID_GET_SEL_TIME = 0x48
CMDID_SET_SEL_TIME = 0x49
CMDID_GET_AUXILIARY_LOG_STATUS = 0x5a
CMDID_SET_AUXILIARY_LOG_STATUS = 0x5b
CMDID_GET_SEL_TIME_UTC_OFFSET = 0x5c
CMDID_SET_SEL_TIME_UTC_OFFSET = 0x5d
CMDID_SET_LAN_CONFIGURATION_PARAMETERS = 0x01
CMDID_GET_LAN_CONFIGURATION_PARAMETERS = 0x02
CMDID_SUSPEND_BMC_ARPS = 0x03
CMDID_GET_IP_UDP_RMCP_STATISTICS = 0x04
CMDID_GET_PICMG_PROPERTIES = 0x00
CMDID_GET_ADDRESS_INFO = 0x01
CMDID_GET_SHELF_ADDRESS_INFO = 0x02
CMDID_SET_SHELF_ADDRESS_INFO = 0x03
CMDID_FRU_CONTROL = 0x04
CMDID_GET_FRU_LED_PROPERTIES = 0x05
CMDID_GET_FRU_LED_COLOR_CAPABILITIES = 0x06
CMDID_SET_FRU_LED_STATE = 0x07
CMDID_GET_FRU_LED_STATE = 0x08
CMDID_SET_IPMB_STATE = 0x09
CMDID_SET_FRU_ACTIVATION_POLICY = 0x0a
CMDID_GET_FRU_ACTIVATION_POLICY = 0x0b
CMDID_SET_FRU_ACTIVATION = 0x0c
CMDID_GET_DEVLOC_RECORD_ID = 0x0d
CMDID_SET_PORT_STATE = 0x0e
CMDID_GET_PORT_STATE = 0x0f
CMDID_COMPUTE_POWER_PROPERTIES = 0x10
CMDID_SET_POWER_LEVEL = 0x11
CMDID_GET_POWER_LEVEL = 0x12
CMDID_RENEGOTIATE_POWER = 0x13
CMDID_GET_FAN_SPEED_PROPERTIES = 0x14
CMDID_SET_FAN_LEVEL = 0x15
CMDID_GET_FAN_LEVEL = 0x16
CMDID_BUSED_RESOURCE = 0x17
CMDID_GET_IPMB_LINK_INFO = 0x18
CMDID_GET_SHELF_MANAGER_IPMB_ADDRESS = 0x1b
CMDID_SET_FAN_POLICY = 0x1c
CMDID_GET_FAN_POLICY = 0x1d
CMDID_FRU_CONTROL_CAPABILITIES = 0x1e
CMDID_FRU_INVENTORY_DEVICE_LOCK_CONTROL = 0x1f
CMDID_FRU_INVENTORY_DEVICE_WRITE = 0x20
CMDID_GET_SHELF_MANAGER_IP_ADDRESSES = 0x21
CMDID_GET_SHELF_POWER_ALLOCATION = 0x22
CMDID_SET_CHANNEL_SIGNALING_CLASS = 0x3b
CMDID_GET_CHANNEL_SIGNALING_CLASS = 0x3c
CMDID_SET_AMC_PORT_STATE = 0x19
CMDID_GET_AMC_PORT_STATE = 0x1a
CMDID_GET_LOCATION_INFO = 0x23
CMDID_POWER_CHANNEL_CONTROL = 0x24
CMDID_GET_POWER_CHANNEL_STATUS = 0x25
CMDID_PM_RESET = 0x26
CMDID_GET_PM_STATUS = 0x27
CMDID_PM_HEARTBEAT = 0x28
CMDID_GET_TELCO_ALARM_CAPABILITY = 0x29
CMDID_SET_TELCO_ALARM_STATE = 0x2a
CMDID_GET_TELCO_ALARM_STATE = 0x2b
CMDID_HPM_GET_TARGET_UPGRADE_CAPABILITIES = 0x2e
CMDID_HPM_GET_COMPONENT_PROPERTIES = 0x2f
CMDID_HPM_ABORT_FIRMWARE_UPGRADE = 0x30
CMDID_HPM_INITIATE_UPGRADE_ACTION = 0x31
CMDID_HPM_UPLOAD_FIRMWARE_BLOCK = 0x32
CMDID_HPM_FINISH_FIRMWARE_UPLOAD = 0x33
CMDID_HPM_GET_UPGRADE_STATUS = 0x34
CMDID_HPM_ACTIVATE_FIRMWARE = 0x35
CMDID_HPM_QUERY_SELFTEST_RESULTS = 0x36
CMDID_HPM_QUERY_ROLLBACK_STATUS = 0x37
CMDID_HPM_INITIATE_MANUAL_ROLLBACK = 0x38
CMDID_VITA_GET_VSO_CAPABILITIES = 0x00
CMDID_GET_DCMI_CAPABILITIES_INFO = 0x01
CMDID_GET_POWER_READING = 0x02
CMDID_GET_POWER_LIMIT = 0x03
CMDID_SET_POWER_LIMIT = 0x04
CMDID_ACTIVATE_DEACTIVATE_POWER_LIMIT = 0x05
CMDID_GET_ASSET_TAG = 0x06
CMDID_GET_DCMI_SENSOR_INFO = 0x07
CMDID_SET_ASSET_TAG = 0x08
CMDID_GET_MANAGEMENT_CONTROLLER_ID_STRING = 0x09
CMDID_SET_MANAGEMENT_CONTROLLER_ID_STRING = 0x0a
REPOSITORY_INITIATE_ERASE = 0xaa
REPOSITORY_GET_ERASE_STATUS = 0x00
REPOSITORY_ERASURE_IN_PROGRESS = 0x0
REPOSITORY_ERASURE_COMPLETED = 0x1 EVENT_ASSERTION = 0 EVENT_DEASSERTION = 1 def clear_repository_helper(reserve_fn, clear_fn, retry=5, reservation=None):
    """Helper function to start repository erasure and wait until finish.

    This helper is used by clear_sel and clear_sdr_repository.
    """
    if reservation is None:
        reservation = reserve_fn()

    # start erasure
    reservation = _clear_repository(reserve_fn, clear_fn,
                                    constants.REPOSITORY_INITIATE_ERASE,
                                    retry, reservation)

    # give some time to clear
    time.sleep(0.5)

    # wait until finish
    reservation = _clear_repository(reserve_fn, clear_fn,
                                    constants.REPOSITORY_GET_ERASE_STATUS,
                                    retry, reservation) class State(DefaultProperties, ResponseDecoder):
    """This is a container that represents a state.

    The state can have default
    properties that are created by init.
    """

    def __init__(self, rsp=None):
        DefaultProperties.__init__(self)
        ResponseDecoder.__init__(self, rsp)
from array import array
from .errors import CompletionCodeError, DecodingError
from .utils import check_completion_code, ByteBuffer
from .msgs import create_request_by_name
from .msgs import constants
from .event import EVENT_ASSERTION, EVENT_DEASSERTION
from .helper import clear_repository_helper
from .state import State
                    continue
                else:
                    raise
            return sel_entry

    def get_sel_entry(self, record_id, reservation=0):
        ENTIRE_RECORD = 0xff
        req = create_request_by_name('GetSelEntry')
        req.reservation_id = reservation
        req.record_id = record_id
        req.offset = 0
        self.max_req_len = ENTIRE_RECORD

        record_data = ByteBuffer()

        while True:
            req.length = self.max_req_len
            if (self.max_req_len != 0xff
                    and (req.offset + req.length) > 16):
                req.length = 16 - req.offset

            rsp = self.send_message(req)
            if rsp.completion_code == constants.CC_CANT_RET_NUM_REQ_BYTES:
                if self.max_req_len == 0xff:
                    self.max_req_len = 16
                else:
                    self.max_req_len -= 1
                continue
            else:
                check_completion_code(rsp.completion_code)

            record_data.extend(rsp.record_data)
            req.offset = len(record_data)

            if len(record_data) >= 16:
                break

        return (SelEntry(record_data), rsp.next_record_id)

    def sel_entries(self):
        """Generator which returns all SEL entries."""
        START_SEL_RECORD_ID = 0
        END_SEL_RECORD_ID = 0xffff
        if self.get_sel_entries_count() == 0:
            return

        reservation_id = self.get_sel_reservation_id()
        next_record_id = START_SEL_RECORD_ID
        while True:
            (sel_entry, next_record_id) = self.get_sel_entry(next_record_id,
                                                             reservation_id)
            yield sel_entry
            if next_record_id == END_SEL_RECORD_ID:
                break

    def get_sel_entries(self):
        """Return all SEL entries as a list."""
        return list(self.sel_entries())


class SelInfo(State):

    def _from_response(self, rsp):
        self.version = rsp.version
        self.entries = rsp.entries
        self.free_bytes = rsp.free_bytes
        self.most_recent_addition = rsp.most_recent_addition
        self.most_recent_erase = rsp.most_recent_erase
        self.operation_support = []
        if rsp.operation_support.get_sel_allocation_info:
            self.operation_support.append('get_sel_allocation_info')
        if rsp.operation_support.reserve_sel:
            self.operation_support.append('reserve_sel')
        if rsp.operation_support.partial_add_sel_entry:
            self.operation_support.append('partial_add_sel_entry')
        if rsp.operation_support.delete_sel:
            self.operation_support.append('delete_sel')
        if rsp.operation_support.overflow_flag:
            self.operation_support.append('overflow_flag')


class SelEntry(State):
    TYPE_SYSTEM_EVENT = 0x02
    TYPE_OEM_TIMESTAMPED_RANGE = list(range(0xc0, 0xe0))
    TYPE_OEM_NON_TIMESTAMPED_RANGE = list(range(0xe0, 0x100))

    def __str__(self):
        raw = '[%s]' % (' '.join(['0x%02x' % b for b in self.data]))
        string = []
        string.append('SEL Record ID 0x%04x' % self.record_id)
        string.append('  Raw: %s' % raw)
        string.append('  Type: %d' % self.type)
        string.append('  Timestamp: %d' % self.timestamp)
        string.append('  Generator: %d' % self.generator_id)
        string.append('  EvM rev: %d' % self.evm_rev)
        string.append('  Sensor Type: 0x%02x' % self.sensor_type)
        string.append('  Sensor Number: %d' % self.sensor_number)
        string.append('  Event Direction: %d' % self.event_direction)
        string.append('  Event Type: 0x%02x' % self.event_type)
        string.append('  Event Data: %s' % array('B', self.event_data).tolist())
        return "\n".join(string)

    @staticmethod
    def type_to_string(entry_type):
        string = None
        if entry_type == SelEntry.TYPE_SYSTEM_EVENT:
            string = 'System Event'
        elif entry_type in SelEntry.TYPE_OEM_TIMESTAMPED_RANGE:
            string = 'OEM timestamped (0x%02x)' % entry_type
        elif entry_type in SelEntry.TYPE_OEM_NON_TIMESTAMPED_RANGE:
            string = 'OEM non-timestamped (0x%02x)' % entry_type
        return string

    def _from_response(self, data):
        if len(data) != 16:
            raise DecodingError('Invalid SEL record length (%d)' % len(data))

        self.data = data

        # pop will change data, therefore copy it

        buffer = ByteBuffer(data)