#!/usr/bin/env python3 # -*- coding: utf-8 -*- ########################################################################################################################################## # Python-File Information # ########################################################################################################################################## # # File: Xcom_API.py # Author: Tobias Müller # Date: 2017.08.09 # ########################################################################################################################################## # Requirements # ########################################################################################################################################## import struct ########################################################################################################################################## # Class Definition & Description # ########################################################################################################################################## # This class is used to generate byte-frames, which are needed to communicate with Xtender-Moduls over the RS232-BUS with the Xcom-232i. # After generating the byte-frame, you can use "pyserial" to communicate with the Serial-BUS and to put the byte-frame into the write- # method of "pyserial". You can decode also read-frames of the Serial-BUS with this class. # # This class contains methods to generate read- or write-frames, and methods to decode frames. There are pre-defined types, properties # and formats for the most important parameter- and information-numbers. This reduces the effort for generating byte-frames, but there # are extended methods to generate byte-frames with other parameter- and information-numbers. class Xcom_API(): """ This class is used to generate byte-frames, which are needed to communicate with Xtender-Moduls over the RS232-BUS with the Xcom-232i. After generating the byte-frame, you can use "pyserial" to communicate with the Serial-BUS and to put the byte-frame into the write- method of "pyserial". You can decode also read-frames of the Serial-BUS with this class. This class contains methods to generate read- or write-frames, and methods to decode frames. There are pre-defined types, properties and formats for the most important parameter- and information-numbers. This reduces the effort for generating byte-frames, but there are extended methods to generate byte-frames with other parameter- and information-numbers. """ ################################################################################################################################## # Program Information # ################################################################################################################################## __PROG_NAME = 'Xcom_API' __PROG_Version = 'v1.0' ################################################################################################################################## # Private-Object-Instance-Counter # ################################################################################################################################## __object_counter = 0 ################################################################################################################################## # Private-Attributes-Service_ID # ################################################################################################################################## __service_read = 1 __service_write = 2 ################################################################################################################################## # Protected-Attributes-Object_Type # ################################################################################################################################## _object_type_info = 1 _object_type_parameter = 2 _object_type_message = 3 _object_type_datalog_field = 5 _object_type_datalog_transfer = 257 ################################################################################################################################## # Protected-Attributes-Info-Object_ID # ################################################################################################################################## _info_battery_voltage = 3000 _info_battery_temperature = 3001 _info_battery_charge_current = 3005 _info_battery_voltage_ripple = 3006 _info_state_of_charge = 3007 _info_number_of_battery_elements = 3050 _info_input_voltage = 3011 _info_input_current = 3012 _info_input_frequency = 3084 _info_input_power = 3138 _info_output_voltage = 3021 _info_output_current = 3022 _info_output_frequency = 3085 _info_output_power = 3139 _info_operating_state = 3028 _info_boost_active = 3019 _info_state_of_inverter = 3049 _info_state_of_transfer_relay = 3020 _info_state_of_output_relay = 3030 _info_state_of_aux_relay_1 = 3031 _info_state_of_aux_relay_2 = 3032 _info_state_of_ground_relay = 3074 _info_state_of_neutral_transfer_relay = 3075 _info_state_of_remote_entry = 3086 _info_battery_voltage_slr = 11000 _info_battery_charge_current_slr = 11001 _info_pv_voltage_slr = 11002 _info_pv_power_slr = 11004 _info_operation_mode_slr = 11016 _info_type_of_error_slr = 11034 _info_battery_cycle_phase_slr = 11038 _info_state_variotrack_slr = 11069 ################################################################################################################################## # Protected-Attributes-Parameter-Object_ID # ################################################################################################################################## _para_maximum_current_of_ac_source = 1107 _para_battery_charge_current = 1138 _para_smart_boost_allowed = 1126 _para_inverter_allowed = 1124 _para_type_of_detection_of_grid_loss = 1552 _para_charger_allowed = 1125 _para_charger_uses_only_power_from_ac = 1646 _para_ac_output_voltage = 1286 _para_inverter_frequency = 1112 _para_transfer_relay_allowed = 1128 _para_limitation_of_the_power_boost = 1607 _para_remote_entry_active = 1545 _para_battery_charge_current_slr = 10002 _para_battery_undervoltage_slr = 10334 _para_operating_mode_aux_1_slr = 10089 ################################################################################################################################## # Protected-Attributes-Property_ID # ################################################################################################################################## _property_id_value = 1 _property_id_string = 1 _property_id_value_qsp = 5 _property_id_min_qsp = 6 _property_id_max_qsp = 7 _property_id_level_qsp = 8 _property_id_unsaved_value_qsp = 13 _property_id_invalid_Action = 0 _property_id_sd_start = 21 _property_id_sd_datablock = 22 _property_id_sd_ack_continue = 23 _property_id_sd_nack_retry = 24 _property_id_sd_abort = 25 _property_id_sd_finish = 26 ################################################################################################################################## # Protected-Attributes-Format # ################################################################################################################################## _format_bool = [1,1] _format_format = [2,2] _format_short_int = [3,2] _format_enum = [4,2] _format_short_enum = [5,2] _format_long_enum = [6,4] _format_error = [7,2] _format_int32 = [8,4] _format_float = [9,4] _format_byte = [10,1] ################################################################################################################################## # Private-Level-QSP # ################################################################################################################################## __level_qsp_view_only = 0x0000 __level_qsp_basic = 0x0010 __level_qsp_expert = 0x0020 __level_qsp_installer = 0x0030 __level_qsp_qsp = 0x0040 ################################################################################################################################## # Private-Attributes # ################################################################################################################################## __start_byte = 0xAA __frame_flags = 0x00 __data_flags = 0x00 __data_frame = 0x0A # Dictionary of Error-Codes-Descriptions __error_code_dict = {0x0001 : 'INVALID_FRAME', 0x0002 : 'DEVICE_NOT_FOUND', 0x0003 : 'RESPONSE_TIMEOUT', 0x0011 : 'SERVICE_NOT_SUPPORTED', 0x0012 : 'INVALID_SERVICE_ARGUMENT', 0x0013 : 'SCOM_ERROR_GATEWAY_BUSY', 0x0021 : 'TYPE_NOT_SUPPORTED', 0x0022 : 'OBJECT_ID_NOT_FOUND', 0x0023 : 'PROPERTY_NOT_SUPPORTED', 0x0024 : 'INVALID_DATA_LENGTH', 0x0025 : 'PROPERTY_IS_READ_ONLY', 0x0026 : 'INVALID_DATA', 0x0027 : 'DATA_TOO_SMALL', 0x0028 : 'DATA_TO_BIG', 0x0029 : 'WRITE_PROPERTY_FAILED', 0x002A : 'READ_PROPERTY_FAILED', 0X002B : 'ACCESS_DENIED', 0x002C : 'SCOM_ERROR_OBJECT_NOT_SUPPORTED', 0x002D : 'SCOM_ERROR_MULTICAST_READ_NOT_SUPPORTED', 0x002E : 'OBJECT_PROPERTY_INVALID', 0x002F : 'FILE_OR_DIR_NOT_PRESENT', 0x0030 : 'FILE_CORRUPTED', 0x0081 : 'INVALID_SHELL_ARG'} # Dictionary of Parameter-Info-Numbers with type, property and format __para_info_dict = {3000 : [_object_type_info,_property_id_value,_format_float], 3001 : [_object_type_info,_property_id_value,_format_float], 3005 : [_object_type_info,_property_id_value,_format_float], 3006 : [_object_type_info,_property_id_value,_format_float], 3007 : [_object_type_info,_property_id_value,_format_float], 3050 : [_object_type_info,_property_id_value,_format_float], 3011 : [_object_type_info,_property_id_value,_format_float], 3012 : [_object_type_info,_property_id_value,_format_float], 3084 : [_object_type_info,_property_id_value,_format_float], 3138 : [_object_type_info,_property_id_value,_format_float], 3020 : [_object_type_info,_property_id_value,_format_short_enum], 3021 : [_object_type_info,_property_id_value,_format_float], 3022 : [_object_type_info,_property_id_value,_format_float], 3085 : [_object_type_info,_property_id_value,_format_float], 3139 : [_object_type_info,_property_id_value,_format_float], 3028 : [_object_type_info,_property_id_value,_format_short_enum], 3019 : [_object_type_info,_property_id_value,_format_short_enum], 3049 : [_object_type_info,_property_id_value,_format_short_enum], 3030 : [_object_type_info,_property_id_value,_format_short_enum], 3031 : [_object_type_info,_property_id_value,_format_short_enum], 3032 : [_object_type_info,_property_id_value,_format_short_enum], 3074 : [_object_type_info,_property_id_value,_format_short_enum], 3075 : [_object_type_info,_property_id_value,_format_short_enum], 3086 : [_object_type_info,_property_id_value,_format_short_enum], 1107 : [_object_type_parameter,_property_id_value_qsp,_format_float], 1138 : [_object_type_parameter,_property_id_value_qsp,_format_float], 1126 : [_object_type_parameter,_property_id_value_qsp,_format_bool], 1124 : [_object_type_parameter,_property_id_value_qsp,_format_bool], 1552 : [_object_type_parameter,_property_id_value_qsp,_format_long_enum], 1125 : [_object_type_parameter,_property_id_value_qsp,_format_bool], 1646 : [_object_type_parameter,_property_id_value_qsp,_format_bool], 1286 : [_object_type_parameter,_property_id_value_qsp,_format_float], 1112 : [_object_type_parameter,_property_id_value_qsp,_format_float], 1128 : [_object_type_parameter,_property_id_value_qsp,_format_bool], 1607 : [_object_type_parameter,_property_id_value_qsp,_format_float], 1545 : [_object_type_parameter,_property_id_value_qsp,_format_long_enum], 10002 : [_object_type_parameter,_property_id_value,_format_float], 10334 : [_object_type_parameter,_property_id_value,_format_float], 10089 : [_object_type_parameter,_property_id_value,_format_long_enum], 11000 : [_object_type_info,_property_id_value,_format_float], 11001 : [_object_type_info,_property_id_value,_format_float], 11002 : [_object_type_info,_property_id_value,_format_float], 11004 : [_object_type_info,_property_id_value,_format_float], 11016 : [_object_type_info,_property_id_value,_format_short_enum], 11034 : [_object_type_info,_property_id_value,_format_short_enum], 11038 : [_object_type_info,_property_id_value,_format_short_enum], 11069 : [_object_type_info,_property_id_value,_format_short_enum]} ################################################################################################################################## # Constructor-Method # ################################################################################################################################## # This Method is used with generating an object of the Xcom_API-Class. There are pre-defined arguments (CRC-Check, source-address, # destination-address) for this object, when you generate an objects without relevant arguments. def __init__(self, crc = True, source = 1, destination = 101): """ ### Description: This Method is used with generating an object of the Xcom_API-Class. There are predefined arguments (CRC-Check, source-address, destination-address) for this object, when you generate an objects without relevant arguments. ### Arguments: There are predefined arguments, which you can change: +-+-+ | | | +=+=+ | **crc** | = True | +-+-+ | **source** | = 1 | +-+-+ | **destination** | = 101 | +-+-+ ### Return-Value: This Method return an object of the Xcom_API-Class. ### Example-Code: ```>>> Object = Xcom_API() **Or if you want to change predefined arguments:** ```>>> Object = Xcom_API(crc = False) """ # Check the type of the argument "crc". It raises a "ValueError" with a false type. if not isinstance(crc, bool): raise ValueError('CRC is not type \"bool\"!') # Check the type of the argument "source". It raises a "ValueError" with a false type. elif not isinstance(source, int) or isinstance(source, bool): raise ValueError('Source-Address is not type \"int\"!') # Check the range of the argument "source" It raises a "ValueError", if the argument is out of range. elif not 0>> Object = Xcom_API() >>> del Object """ if hasattr(self,'__crc'): del self.__crc if hasattr(self,'__source'): del self.__source if hasattr(self,'__dest'): del self.__dest if hasattr(self,'__frame_check_done'): del self.__frame_check_done Xcom_API.__object_counter -= 1 ################################################################################################################################## # Information-Methods # ################################################################################################################################## # This method returns an integer-value of the source-address of an object. def get_source_address(self): """ ### Description: This method returns an integer-value of the source-address of an object. ### Return-Value: This Method return an **int**. ### Example-Code: ```>>> Object = Xcom_API() >>> Src_Addr = Object.get_source_address() >>> Src_Addr 1 """ return self.__source # This method returns an integer-value of the destination-address of an object. def get_destination_address(self): """ ### Description: This method returns an integer-value of the destination-address of an object. ### Return-Value: This Method return an **int**. ### Example-Code: ```>>> Object = Xcom_API() >>> Dest_Addr = Object.get_destination_address() >>> Dest_Addr 101 """ return self.__dest # This method returns a boolean-value of the active-state of the CRC-Check of an object. def is_crc_check_active(self): """ ### Description: This method returns a boolean-value of the active-state of the CRC-Check of an object. ### Return-Value: This Method return a **boolean**. ### Example-Code: ```>>> Object = Xcom_API() >>> CRC_Active = Object.is_crc_check_active() >>> CRC_Active True """ return self.__crc # This method returns a integer-value of the counter of active-objects of this class. @staticmethod def get_object_counter(): """ ### Description: This method returns a integer-value of the counter of active-objects of this class. ### Return-Value: This Method return an **int**. ### Example-Code: ```>>> Obj_Count = Xcom_API.get_object_counter() >>> Obj_Count 0 **Or if you have generate an Object:** ```>>> Object = Xcom_API() >>> Obj_Count = Object.get_object_counter() >>> Obj_Count 1 """ return Xcom_API.__object_counter # This method returns a string of the of the program-name of this class. @staticmethod def get_prog_name(): """ ### Description: This method returns a string of the of the program-name of this class. ### Return-Value: This Method return a **string**. ### Example-Code: ```>>> Prog_Name = Xcom_API.get_prog_name() >>> Prog_Name 'Xcom_API' **Or if you have generate an Object:** ```>>> Object = Xcom_API() >>> Prog_Name = Object.get_prog_name() >>> Prog_Name 'Xcom_API' """ return Xcom_API.__PROG_NAME # This method returns a string of the of the program-version of this class. @staticmethod def get_prog_version(): """ ### Description: This method returns a string of the of the program-version of this class. ### Return-Value: This Method return a **string**. ### Example-Code: ```>>> Prog_Version = Xcom_API.get_prog_version >>> Prog_Version 'v1.0' **Or if you have generate an Object:** ```>>> Object = Xcom_API() >>> Prog_Version = Object.get_prog_version >>> Prog_Version 'v1.0' """ return Xcom_API.__PROG_Version ################################################################################################################################## # Private-Methods # ################################################################################################################################## # This Method calculate the CRC-Values of a Frame. It returns the CRC-Values in a list or bytearray. def __calculate_checksum(byte_frame): """ ### Description: This Method calculate the CRC-Values of a Frame. It returns the CRC-Values in a **list**. ### Arguments: The **** argument must be a type of **list** or **bytearray**. ### Return-Value: This Method return a **list** of length two with the CRC-Values. ### Example-Code: ```>>> Byte_Frame = [0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0xA, 0x0] >>> CRC_Value = Xcom_API.__calculate_checksum(Byte_Frame) >>> CRC_Value [0x6F, 0x71] **Or if you are not using this function inside the API:** ```>>> Byte_Frame = [0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0xA, 0x0] >>> CRC_Value = Xcom_API._Xcom_API__calculate_checksum(Byte_Frame) >>> CRC_Value [0x6F, 0x71] """ # Define two Buffers for the CRC-Values. Buffer1 = 0xFF Buffer2 = 0 # Calculate the CRC-Values of a Frame. for i in range(0,len(byte_frame)): Buffer1 = (Buffer1 + byte_frame[i]) % 256 Buffer2 = (Buffer1 + Buffer2) % 256 # Return the CRC-Values in a list. return [Buffer1, Buffer2] # This Method generate a Byte-Frame of a Value depending of the format. It returns a list of Bytes, which are ordered in LSB to # MSB. def __value_to_byte_frame(property_data, data_format): """ ### Description: This Method generate a Byte-Frame of a Value depending of the format. It returns a list of Bytes, which are ordered in LSB to MSB. ### Arguments: The **** argument must be a type of **int** or **float** depending of the type of ****. For the **** argument use one of the following formats: +-+-+ | | | +=+=+ | _format_bool | = [1, 1] | +-+-+ | _format_format | = [2, 2] | +-+-+ | _format_short_int | = [3, 2] | +-+-+ | _format_enum | = [4, 2] | +-+-+ | _format_short_enum | = [5, 2] | +-+-+ | _format_long_enum | = [6, 4] | +-+-+ | _format_error | = [7, 2] | +-+-+ | _format_int32 | = [8, 4] | +-+-+ | _format_float | = [9, 4] | +-+-+ | _format_byte | = [10, 1] | +-+-+ ### Return-Value: This Method return a **list** with values, which represents the **Byte_Frame**. The length of the **list** depends of the **** argument. ### Example-Code: ```>>> Value = 12.0 >>> Byte_Frame = Xcom_API.__value_to_byte_frame(Value, Xcom_API._format_float) >>> Byte_Frame [0x0, 0x0, 0x40, 0x41] **Or if you are not using this function inside the API:** ```>>> Value = 12.0 >>> Byte_Frame = Xcom_API._Xcom_API__value_to_byte_frame(Value, Xcom_API._format_float) >>> Byte_Frame [0x0, 0x0, 0x40, 0x41] """ # It checks the argument "data_format" for type "Bool" and check the range/type of "property_data", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it returns the list of the Byte-Frame. if data_format==Xcom_API._format_bool: if not 0<=property_data<=1 or not (isinstance(property_data, int) or isinstance(property_data, bool)): raise ValueError('Invalid \"property_data\" for the data_format of type \"bool\"!') else: # Return a list of the Byte-Frame. if property_data == 0 or property_data == False: return [0] else: return [1] # It checks the argument "data_format" for type "Byte" and check the range/type of "property_data", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it returns the list of the Byte-Frame. elif data_format==Xcom_API._format_byte: if not 0<=property_data<256 or not isinstance(property_data, int): raise ValueError('Invalid \"property_data\" for the data_format of type \"Byte\"!') else: # Return a list of the Byte-Frame. return [property_data] # It checks the argument "data_format" for type "Error" and check the range/type of "property_data", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the list of the Byte-Frame. elif data_format==Xcom_API._format_error: if not 0<=property_data<=0x65535 or not isinstance(property_data, int): raise ValueError('Invalid \"property_data\" for the data_format of type \"Error\"!') else: # Define a Buffer for calculating. Buffer = hex(property_data) # Calculate the Byte-Frame. for i in range(0,data_format[1]*2-(len(Buffer)-2)): Buffer = Buffer[0:2]+str(0)+Buffer[2:] # Return a list of the Byte-Frame. return [int(Buffer[4:6],16),int(Buffer[2:4],16)] # It checks the argument "data_format" for type "Format" or "Short Integer" and check the range/type of "property_data", # depending of the data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the list of # the Byte-Frame. elif data_format==Xcom_API._format_format or data_format==Xcom_API._format_short_int: if not -32768<=property_data<=32767 or not isinstance(property_data, int): raise ValueError('Invalid \"property_data\" for the data_format of type \"Format\" or \"Short Integer\"!') else: # Calculate the Byte-Frame. if property_data<0: # Define a Buffer for calculating. Buffer = hex(2**16 + property_data) else: # Define a Buffer for calculating. Buffer = hex(property_data) for i in range(0,data_format[1]*2-(len(Buffer)-2)): Buffer = Buffer[0:2]+str(0)+Buffer[2:] # Return a list of the Byte-Frame. return [int(Buffer[4:6],16),int(Buffer[2:4],16)] # It checks the argument "data_format" for type "Enum" or "Short Enum" and check the range/type of "property_data", depending # of the data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the list of the Byte-Frame. # Only one Bit can be set in data_format of type "Enum". elif data_format==Xcom_API._format_enum or data_format==Xcom_API._format_short_enum: if not 0<=property_data<=32767 or not isinstance(property_data, int): raise ValueError('Invalid \"property_data\" for the data_format of type \"Enum\" or \"Short Enum\"!') else: check = False for i in range(0,16): if (2**i==2**i&property_data) and check: raise ValueError('Invalid \"property_data\" for the data_format of type \"Enum\" or \"Short Enum\"!') elif 2**i==2**i&property_data: check = True # Define a Buffer for calculating. Buffer = hex(property_data) # Calculate the Byte-Frame. for i in range(0,data_format[1]*2-(len(Buffer)-2)): Buffer = Buffer[0:2]+str(0)+Buffer[2:] # Return a list of the Byte-Frame. return [int(Buffer[4:6],16),int(Buffer[2:4],16)] # It checks the argument "data_format" for type "Long Enum" and check the range/type of "property_data", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the list of the Byte-Frame. # Only one Bit can be set in data_format of type "Enum". elif data_format==Xcom_API._format_long_enum: if not 0<=property_data<=2147483647 or not isinstance(property_data, int): raise ValueError('Invalid \"property_data\" for the data_format of type \"Long Enum\"!') else: check = False for i in range(0,32): if (2**i==2**i&property_data) and check: raise ValueError('Invalid \"property_data\" for the data_format of type \"Long Enum\"!') elif 2**i==2**i&property_data: check = True # Define a Buffer for calculating. Buffer = hex(property_data) # Calculate the Byte-Frame. for i in range(0,data_format[1]*2-(len(Buffer)-2)): Buffer = Buffer[0:2]+str(0)+Buffer[2:] # Return a list of the Byte-Frame. return [int(Buffer[8:10],16),int(Buffer[6:8],16),int(Buffer[4:6],16),int(Buffer[2:4],16)] # It checks the argument "data_format" for type "INT32" and check the range/type of "property_data", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the list of the Byte-Frame. elif data_format==Xcom_API._format_int32: if not -2147483648<=property_data<=2147483647 or not isinstance(property_data, int): raise ValueError('Invalid \"property_data\" for the data_format of type \"INT32\"!') else: # Calculate the Byte-Frame. if property_data<0: # Define a Buffer for calculating. Buffer = hex(2**32 + property_data) else: # Define a Buffer for calculating. Buffer = hex(property_data) for i in range(0,data_format[1]*2-(len(Buffer)-2)): Buffer = Buffer[0:2]+str(0)+Buffer[2:] # Return a list of the Byte-Frame. return [int(Buffer[8:10],16),int(Buffer[6:8],16),int(Buffer[4:6],16),int(Buffer[2:4],16)] # It checks the argument "data_format" for type "Float" and check the range/type of "property_data", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the list of the Byte-Frame. elif data_format==Xcom_API._format_float: if not -2147483648<=property_data<=2147483647 or not isinstance(property_data, float): raise ValueError('Invalid \"property_data\" for the data_format of type \"Float\"!') else: # Calculate the Byte-Frame. Buffer = struct.pack("!f",property_data) # Return a list of the Byte-Frame. return [Buffer[3],Buffer[2],Buffer[1],Buffer[0]] # It raises an Error, if the argument "data_format" doesn't fit. else: raise ValueError('data_format is unknown!') # This Method generate a Value of a Byte-Frame depending of the format. It returns the value as a integer/float. def __byte_frame_to_value(byte_frame, data_format): """ ### Description: This Method generate a Value of a Byte-Frame depending of the format. It returns the value as a integer/float. ### Arguments: The **** argument must be a type of **list** or **bytearray**. The lenght depends of the type of ****. For the **** argument use one of the following formats: +-+-+ | | | +=+=+ | _format_bool | = [1, 1] | +-+-+ | _format_format | = [2, 2] | +-+-+ | _format_short_int | = [3, 2] | +-+-+ | _format_enum | = [4, 2] | +-+-+ | _format_short_enum | = [5, 2] | +-+-+ | _format_long_enum | = [6, 4] | +-+-+ | _format_error | = [7, 2] | +-+-+ | _format_int32 | = [8, 4] | +-+-+ | _format_float | = [9, 4] | +-+-+ | _format_byte | = [10, 1] | +-+-+ ### Return-Value: This Method return a value of type **int** or **float** depending of the **** argument. ### Example-Code: ```>>> Byte_Frame = [0x0, 0x0, 0x40, 0x41] >>> Value = Xcom_API.__byte_frame_to_value(Byte_Frame, Xcom_API._format_float) >>> Value 12.0 **Or if you are not using this function inside the API:** ```>>> Byte_Frame = [0x0, 0x0, 0x40, 0x41] >>> Value = Xcom_API._Xcom_API__byte_frame_to_value(Byte_Frame, Xcom_API._format_float) >>> Value 12.0 """ # It checks the argument "data_format" for type "Bool" and check the length/type of "byte_frame", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it returns the value of the Byte-Frame. if data_format==Xcom_API._format_bool: if not len(byte_frame)==data_format[1] or not isinstance(byte_frame[0], int) or not 0<=byte_frame[0]<=1: raise ValueError('Invalid \"byte_frame\" for the data_format of type \"bool\"!') else: # Return the value of the Byte-Frame. return byte_frame[0] # It checks the argument "data_format" for type "Byte" and check the length/type of "byte_frame", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it returns the value of the Byte-Frame. elif data_format==Xcom_API._format_byte: if not len(byte_frame)==data_format[1] or not isinstance(byte_frame[0], int): raise ValueError('Invalid \"byte_frame\" for the data_format of type \"Byte\"!') else: # Return the value of the Byte-Frame. return byte_frame[0] # It checks the argument "data_format" for type "Error" and check the length/type of "byte_frame", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the value of the Byte-Frame. elif data_format==Xcom_API._format_error: if not len(byte_frame)==data_format[1] or not isinstance(byte_frame[0], int) or not isinstance(byte_frame[1], int): raise ValueError('Invalid \"byte_frame\" for the data_format of type \"Error\"!') else: # Define a Buffer. Buffer = '0x' # Calculate the value. for i in range(0,data_format[1]): if len(hex(byte_frame[data_format[1]-1-i]))==4: Buffer = Buffer + hex(byte_frame[data_format[1]-1-i])[2:] else: Buffer = Buffer + '0' + hex(byte_frame[data_format[1]-1-i])[2:] # Return the value of the Byte-Frame. return int(Buffer,16) # It checks the argument "data_format" for type "Format" or "Short Integer" and check the length/type of "byte_frame", depending # of the data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the value of the Byte-Frame. elif data_format==Xcom_API._format_format or data_format==Xcom_API._format_short_int: if not len(byte_frame)==data_format[1] or not isinstance(byte_frame[0], int) or not isinstance(byte_frame[1], int): raise ValueError('Invalid \"byte_frame\" for the data_format of type \"Format\" or \"Short Integer\"!') else: # Define a Buffer. Buffer = '0x' # Calculate the value. for i in range(0,data_format[1]): if len(hex(byte_frame[data_format[1]-1-i]))==4: Buffer = Buffer + hex(byte_frame[data_format[1]-1-i])[2:] else: Buffer = Buffer + '0' + hex(byte_frame[data_format[1]-1-i])[2:] # Return the value of the Byte-Frame. if int(Buffer,16)<32768: return int(Buffer,16) else: return (-65536 + int(Buffer,16)) # It checks the argument "data_format" for type "Enum" or "Short Enum" and check the length/type of "byte_frame", depending of the # data_format. It raises an ValueError if the checks failed, otherwise it calculate and returns the value of the Byte-Frame. elif data_format==Xcom_API._format_enum or data_format==Xcom_API._format_short_enum: if not len(byte_frame)==data_format[1] or not isinstance(byte_frame[0], int) or not isinstance(byte_frame[1], int): raise ValueError('Invalid \"byte_frame\" for the data_format of type \"Enum\" or \"Short Enum\"!') else: # Define a Buffer. Buffer = '0x' # Calculate the value. for i in range(0,data_format[1]): if len(hex(byte_frame[data_format[1]-1-i]))==4: Buffer = Buffer + hex(byte_frame[data_format[1]-1-i])[2:] else: Buffer = Buffer + '0' + hex(byte_frame[data_format[1]-1-i])[2:] # Return the value of the Byte-Frame. if int(Buffer,16)<32768: return int(Buffer,16) else: raise ValueError('Invalid \"byte_frame\" for the data_format of type \"Enum\" or \"Short Enum\"!') # It checks the argument "data_format" for type "Long Enum" and check the length/type of "byte_frame", depending of the data_format. # It raises an ValueError if the checks failed, otherwise it calculate and returns the value of the Byte-Frame. elif data_format==Xcom_API._format_long_enum: if (not len(byte_frame)==data_format[1] or not isinstance(byte_frame[0], int) or not isinstance(byte_frame[1], int) or not isinstance(byte_frame[2], int) or not isinstance(byte_frame[3], int)): raise ValueError('Invalid \"byte_frame" for the data_format of type \"Long Enum\"!') else: # Define a Buffer. Buffer = '0x' # Calculate the value. for i in range(0,data_format[1]): if len(hex(byte_frame[data_format[1]-1-i]))==4: Buffer = Buffer + hex(byte_frame[data_format[1]-1-i])[2:] else: Buffer = Buffer + '0' + hex(byte_frame[data_format[1]-1-i])[2:] # Return the value of the Byte-Frame. if int(Buffer,16)<2147483648: return int(Buffer,16) else: raise ValueError('Invalid \"byte_frame" for the data_format of type \"Long Enum\"!') # It checks the argument "data_format" for type "INT32" and check the length/type of "byte_frame", depending of the data_format. # It raises an ValueError if the checks failed, otherwise it calculate and returns the value of the Byte-Frame. elif data_format==Xcom_API._format_int32: if (not len(byte_frame)==data_format[1] or not isinstance(byte_frame[0], int) or not isinstance(byte_frame[1], int) or not isinstance(byte_frame[2], int) or not isinstance(byte_frame[3], int)): raise ValueError('Invalid \"byte_frame\" for the data_format of type \"INT32\"!') else: # Define a Buffer. Buffer = '0x' # Calculate the value. for i in range(0,data_format[1]): if len(hex(byte_frame[data_format[1]-1-i]))==4: Buffer = Buffer + hex(byte_frame[data_format[1]-1-i])[2:] else: Buffer = Buffer + '0' + hex(byte_frame[data_format[1]-1-i])[2:] # Return the value of the Byte-Frame. if int(Buffer,16)<2147483648: return int(Buffer,16) else: return (-4294967296 + int(Buffer,16)) # It checks the argument "data_format" for type "Float" and check the length/type of "byte_frame", depending of the data_format. # It raises an ValueError if the checks failed, otherwise it calculate and returns the value of the Byte-Frame. elif data_format==Xcom_API._format_float: if (not len(byte_frame)==data_format[1] or not isinstance(byte_frame[0], int) or not isinstance(byte_frame[1], int) or not isinstance(byte_frame[2], int) or not isinstance(byte_frame[3], int)): raise ValueError('Invalid \"byte_frame\" for the data_format of type \"Float\"!') else: # Define a Buffer. Buffer = bytearray([byte_frame[3],byte_frame[2],byte_frame[1],byte_frame[0]]) # Calculate and return the value of the Byte-Frame. return struct.unpack("!f",Buffer)[0] # It raises an Error, if the argument "format" doesn't fit. else: raise ValueError('data_format is unknown!') # This Method does a Frame-Check. It checks the startbyte, Frame-Length, the checksum and the response-flags. It returns a # boolean-value of the result. def __frame_check(self,bytearray_of_frame): """ ### Description: This Method does a Frame-Check. It checks the startbyte, Frame-Length, the checksum and the response-flags. It returns a boolean-value of the result. ### Arguments: The **** argument must be an object. The **** argument is a complete Byte_frame. ### Return-Value: This Method return a **boolean**. ### Example-Code: ```>>> Object = Xcom_API() >>> Byte_Frame = [0xAA, 0x37, 0x65, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xA, 0x0, 0xA6, 0x5E, 0x2, 0x2, 0x2, 0x0, 0x53, 0x4, 0x0, 0x0, 0x5, 0x0, 0x61, 0x3C] >>> Check = Xcom_API.__frame_check(Object,Byte_Frame) >>> Check True **Or if you are not using this function inside the API:** ```>>> Object = Xcom_API() >>> Byte_Frame = [0xAA, 0x37, 0x65, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xA, 0x0, 0xA6, 0x5E, 0x2, 0x2, 0x2, 0x0, 0x53, 0x4, 0x0, 0x0, 0x5, 0x0, 0x61, 0x3C] >>> Check = Xcom_API._Xcom_API__frame_check(Object,Byte_Frame) >>> Check True """ # Define Buffer for Frame_check. Buffer = list(bytearray_of_frame) try: # Check Start-Byte available if not Buffer[0]==0xAA: raise ValueError('Can\'t find Start-Frame (0xAA)') # Check checksum if activated and check Frame-Length if self.__crc and len(Buffer)>=14: # check Header-Checksum if not Xcom_API.__calculate_checksum(Buffer[1:12])==Buffer[12:14]: raise ValueError('Header-Checksum wrong!') # check Frame-Length if not len(Buffer)==(Xcom_API.__byte_frame_to_value(Buffer[10:12],Xcom_API._format_short_int) + 16): raise ValueError('Frame is not complete!') # check Data-Checksum if not Xcom_API.__calculate_checksum(Buffer[14:(len(Buffer)-2)])==Buffer[(len(Buffer)-2):len(Buffer)]: raise ValueError('Data-Checksum wrong!') elif not self.__crc and len(Buffer)>=14: # check Frame-Length if not len(Buffer)==(Xcom_API.__byte_frame_to_value(Buffer[10:12],Xcom_API._format_short_int) + 16): raise ValueError('Frame is not complete!') else: raise ValueError('Frame is not complete!') # Check Response-Flags if not Buffer[14]>0: raise ValueError('Frame is not a Response-Frame!') return True except IndexError: raise ValueError('Frame is not complete!') ################################################################################################################################## # Public-Methods # ################################################################################################################################## # This method is used to generate a Byte-Frame for a 'read'-instruction. It Returns a bytearray. def get_read_frame_ext(self, object_type, object_id, property_id): """ ### Description: This method is used to generate a Byte-Frame for a 'read'-instruction. It Returns a bytearray. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument indicates the type of the ****. The following shows the possibilities: +-+-+ | | | +=+=+ | _object_type_info | = 1 | +-+-+ | _object_type_parameter | = 2 | +-+-+ | _object_type_message | = 3 | +-+-+ | _object_type_datalog_field | = 5 | +-+-+ | _object_type_datalog_transfer | = 257 | +-+-+ The **** argument indicates, which id should be read. You can find a list of all id's in the communication protocoll at http://www.studer-innotec.com/de/downloads/. The **** argument indicates, which type the property_data has, which you want to read. The following shows the possibilities: +-+-+ | | | +=+=+ | _property_id_value | = 1 | +-+-+ | _property_id_string | = 1 | +-+-+ | _property_id_value_qsp | = 5 | +-+-+ | _property_id_min_qsp | = 6 | +-+-+ | _property_id_max_qsp | = 7 | +-+-+ | _property_id_level_qsp | = 8 | +-+-+ | _property_id_unsaved_value_qsp | = 13 | +-+-+ | _property_id_invalid_Action | = 0 | +-+-+ | _property_id_sd_start | = 21 | +-+-+ | _property_id_sd_datablock | = 22 | +-+-+ | _property_id_sd_ack_continue | = 23 | +-+-+ | _property_id_sd_nack_retry | = 24 | +-+-+ | _property_id_sd_abort | = 25 | +-+-+ | _property_id_sd_finish | = 26 | +-+-+ ### Return-Value: This Method return a **bytearray** of the frame. ### Example-Code: ```>>> Object = Xcom_API() >>> input_current = 3116 >>> Byte_frame = Object.get_read_frame_ext(Xcom_API._object_type_info, input_current, Xcom_API._property_id_value) >>> Byte_frame bytearray(b'\\xAA\\x00\\x01\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x0A\\x00\\x6F\\x71\\x00\\x01\\x01\\x00\\x2C\\x0C\\x00\\x00\\x01\\x00\\x3A\\x4D') """ # Create Buffer Buffer = [] # Append Buffer with Start-Byte. Buffer.append(Xcom_API.__start_byte) # Append Buffer with Frame-Flags. Buffer.append(Xcom_API.__frame_flags) # Extend Buffer with Source-Address. Buffer.extend(Xcom_API.__value_to_byte_frame(self.__source,Xcom_API._format_int32)) # Extend Buffer with Destination-Address. Buffer.extend(Xcom_API.__value_to_byte_frame(self.__dest,Xcom_API._format_int32)) # Extend Buffer with Data-Length. Buffer.extend(Xcom_API.__value_to_byte_frame(Xcom_API.__data_frame,Xcom_API._format_short_int)) # Extend Buffer with Header-Checksum. Buffer.extend(Xcom_API.__calculate_checksum(Buffer[1:len(Buffer)])) # Append Buffer with Data-Flags. Buffer.append(Xcom_API.__data_flags) # Append Buffer with Service_ID. Buffer.append(Xcom_API.__service_read) # Extend Buffer with Opject_Type. Buffer.extend(Xcom_API.__value_to_byte_frame(object_type,Xcom_API._format_short_int)) # Extend Buffer with Object_ID. Buffer.extend(Xcom_API.__value_to_byte_frame(object_id,Xcom_API._format_int32)) # Extend Buffer with Property_ID. Buffer.extend(Xcom_API.__value_to_byte_frame(property_id,Xcom_API._format_short_int)) # Extend Buffer with Data-Checksum. Buffer.extend(Xcom_API.__calculate_checksum(Buffer[14:len(Buffer)])) # Return Byte-Frame. return bytearray(Buffer) # This method is used to generate a Byte-Frame for a 'write'-instruction. It Returns a List of Bytes. def get_write_frame_ext(self, object_type, object_id, property_id, property_data, data_format): """ ### Description: This method is used to generate a Byte-Frame for a 'write'-instruction. It Returns a bytearray. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument indicates the type of the ****. The following shows the possibilities: +-+-+ | | | +=+=+ | _object_type_info | = 1 | +-+-+ | _object_type_parameter | = 2 | +-+-+ | _object_type_message | = 3 | +-+-+ | _object_type_datalog_field | = 5 | +-+-+ | _object_type_datalog_transfer | = 257 | +-+-+ The **** argument indicates, which id should be written. You can find a list of all id's in the communication protocoll at http://www.studer-innotec.com/de/downloads/. The **** argument indicates, which type the property_data has, which you want to read. The following shows the possibilities: +-+-+ | | | +=+=+ | _property_id_value | = 1 | +-+-+ | _property_id_string | = 1 | +-+-+ | _property_id_value_qsp | = 5 | +-+-+ | _property_id_min_qsp | = 6 | +-+-+ | _property_id_max_qsp | = 7 | +-+-+ | _property_id_level_qsp | = 8 | +-+-+ | _property_id_unsaved_value_qsp | = 13 | +-+-+ | _property_id_invalid_Action | = 0 | +-+-+ | _property_id_sd_start | = 21 | +-+-+ | _property_id_sd_datablock | = 22 | +-+-+ | _property_id_sd_ack_continue | = 23 | +-+-+ | _property_id_sd_nack_retry | = 24 | +-+-+ | _property_id_sd_abort | = 25 | +-+-+ | _property_id_sd_finish | = 26 | +-+-+ The **** argument is the value you want to send. The format depends of the **** argument. For the **** argument use one of the following formats: +-+-+ | | | +=+=+ | _format_bool | = [1, 1] | +-+-+ | _format_format | = [2, 2] | +-+-+ | _format_short_int | = [3, 2] | +-+-+ | _format_enum | = [4, 2] | +-+-+ | _format_short_enum | = [5, 2] | +-+-+ | _format_long_enum | = [6, 4] | +-+-+ | _format_error | = [7, 2] | +-+-+ | _format_int32 | = [8, 4] | +-+-+ | _format_float | = [9, 4] | +-+-+ | _format_byte | = [10, 1] | +-+-+ ### Return-Value: This Method return a **bytearray** of the frame. ### Example-Code: ```>>> Object = Xcom_API() >>> Equalization_current = 1290 >>> Data_in_Ampere = 120.0 >>> Byte_frame = Object.get_write_frame_ext(Xcom_API._object_type_parameter, Equalization_current, Xcom_API._property_id_value, Data_in_Ampere, Xcom_API._format_float) >>> Byte_frame bytearray(b'\\xAA\\x00\\x01\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x0E\\x00\\x73\\x79\\x00\\x02\\x02\\x00\\x0A\\x05\\x00\\x00\\x01\\x00\\x00\\x00\\xF0\\x42\\x45\\xDD') """ # Create Buffer Buffer = [] # Append Buffer with Start-Byte. Buffer.append(Xcom_API.__start_byte) # Append Buffer with Frame-Flags. Buffer.append(Xcom_API.__frame_flags) # Extend Buffer with Source-Address. Buffer.extend(Xcom_API.__value_to_byte_frame(self.__source,Xcom_API._format_int32)) # Extend Buffer with Destination-Address. Buffer.extend(Xcom_API.__value_to_byte_frame(self.__dest,Xcom_API._format_int32)) # Extend Buffer with Data-Length. Buffer.extend(Xcom_API.__value_to_byte_frame(Xcom_API.__data_frame + data_format[1],Xcom_API._format_short_int)) # Extend Buffer with Header-Checksum. Buffer.extend(Xcom_API.__calculate_checksum(Buffer[1:len(Buffer)])) # Append Buffer with Data-Flags. Buffer.append(Xcom_API.__data_flags) # Append Buffer with Service_ID. Buffer.append(Xcom_API.__service_write) # Extend Buffer with Opject_Type. Buffer.extend(Xcom_API.__value_to_byte_frame(object_type,Xcom_API._format_short_int)) # Extend Buffer with Object_ID. Buffer.extend(Xcom_API.__value_to_byte_frame(object_id,Xcom_API._format_int32)) # Extend Buffer with Property_ID. Buffer.extend(Xcom_API.__value_to_byte_frame(property_id,Xcom_API._format_short_int)) # Extend Buffer with Property_Data. Buffer.extend(Xcom_API.__value_to_byte_frame(property_data,data_format)) # Extend Buffer with Data-Checksum. Buffer.extend(Xcom_API.__calculate_checksum(Buffer[14:len(Buffer)])) # Return Byte-Frame. return bytearray(Buffer) # This method is used to decode Data from the received Byte-Frame, what you get from the xtender-system. # It returns a List with the result of the returned Data and the Data itself. def get_data_from_frame_ext(self, bytearray_of_frame, data_format): """ ### Description: This method is used to decode Data from the received Byte-Frame, what you get from the xtender-system. If CRC-Check is active, it will check the byte-frame and raises a Value_Error, if a CRC-Error was detected. It returns a List with the result of the returned Data and the Data itself. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument is the frame what you get, when you receive data from the serial port. The **** argument is needed to decode the value. For the **** argument use one of the following formats: +-+-+ | | | +=+=+ | _format_bool | = [1, 1] | +-+-+ | _format_format | = [2, 2] | +-+-+ | _format_short_int | = [3, 2] | +-+-+ | _format_enum | = [4, 2] | +-+-+ | _format_short_enum | = [5, 2] | +-+-+ | _format_long_enum | = [6, 4] | +-+-+ | _format_error | = [7, 2] | +-+-+ | _format_int32 | = [8, 4] | +-+-+ | _format_float | = [9, 4] | +-+-+ | _format_byte | = [10, 1] | +-+-+ ### Return-Value: This Method return a **list** with two elements. The First is a **boolean** value, which is **True**, if the xtender-system detects an error and the second element of the **list** then contains the error-id. If no error occures, then the first element is **False** und the second element contains the answer of your request. The returned value of the second element is a **string**, **int** or **float**, depending of the service (read/write) of the request frame and of the **** argument. ### Example-Code: ```>>> Object = Xcom_API() >>> Frame = bytearray(b'\\xAA\\x37\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\xAA\\x66\\x02\\x01\\x02\\x00\\x53\\x04\\x00\\x00\\x05\\x00\\x00\\xD0\\x14\\x42\\x86\\x8D') >>> Answer = Object.get_data_from_frame_ext(Frame, Xcom_API._format_float) >>> Answer [False, 37.203125] """ # Do a Frame-Check. If it fails, it returns a ValueError. if not self.__frame_check_done: if not Xcom_API.__frame_check(self, bytearray_of_frame): raise ValueError('Frame-Check failed!') self.__frame_check_done = False # Check Frame for an Error and return the value. if bytearray_of_frame[14] == 2: # Check if property_data available if len(bytearray_of_frame) == 26: return [False,'value_set'] else: return [False, Xcom_API.__byte_frame_to_value(bytearray_of_frame[24:(24+data_format[1])],data_format)] else: return [True, Xcom_API.__byte_frame_to_value(bytearray_of_frame[24:(24+Xcom_API._format_error[1])],Xcom_API._format_error)] # This method is used to generate a Byte-Frame for a 'read'-instruction. It can only be used with a known Object_ID, otherwise it # will raise a Value_Error. It Returns a bytearray. def get_read_frame(self, object_id): """ ### Description: This method is used to generate a Byte-Frame for a 'read'-instruction. It can only be used with a known Object_ID, otherwise it will raise a Value_Error. It Returns a bytearray. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument indicates, which id should be read. The following shows the possibilities: +-+-+ | Information-Numbers | | +=+=+ | _info_battery_voltage | = 3000 | +-+-+ | _info_battery_temperature | = 3001 | +-+-+ | _info_battery_charge_current | = 3005 | +-+-+ | _info_battery_voltage_ripple | = 3006 | +-+-+ | _info_state_of_charge | = 3007 | +-+-+ | _info_number_of_battery_elements | = 3050 | +-+-+ | _info_input_voltage | = 3011 | +-+-+ | _info_input_current | = 3012 | +-+-+ | _info_input_frequency | = 3084 | +-+-+ | _info_input_power | = 3138 | +-+-+ | _info_output_voltage | = 3021 | +-+-+ | _info_output_current | = 3022 | +-+-+ | _info_output_frequency | = 3085 | +-+-+ | _info_output_power | = 3139 | +-+-+ | _info_operating_state | = 3028 | +-+-+ | _info_boost_active | = 3019 | +-+-+ | _info_state_of_inverter | = 3049 | +-+-+ | _info_state_of_transfer_relay | = 3020 | +-+-+ | _info_state_of_output_relay | = 3030 | +-+-+ | _info_state_of_aux_relay_1 | = 3031 | +-+-+ | _info_state_of_aux_relay_2 | = 3032 | +-+-+ | _info_state_of_ground_relay | = 3074 | +-+-+ | _info_state_of_neutral_transfer_relay | = 3075 | +-+-+ | _info_state_of_remote_entry | = 3086 | +-+-+ +-+-+ | Parameter-Numbers | | +=+=+ | _para_maximum_current_of_ac_source | = 1107 | +-+-+ | _para_battery_charge_current | = 1138 | +-+-+ | _para_smart_boost_allowed | = 1126 | +-+-+ | _para_inverter_allowed | = 1124 | +-+-+ | _para_type_of_detection_of_grid_loss | = 1552 | +-+-+ | _para_charger_allowed | = 1125 | +-+-+ | _para_charger_uses_only_power_from_ac | = 1646 | +-+-+ | _para_ac_output_voltage | = 1286 | +-+-+ | _para_inverter_frequency | = 1112 | +-+-+ | _para_transfer_relay_allowed | = 1128 | +-+-+ | _para_limitation_of_the_power_boost | = 1607 | +-+-+ | _para_remote_entry_active | = 1545 | +-+-+ ### Return-Value: This Method return a **bytearray** of the frame. ### Example-Code: ```>>> Object = Xcom_API() >>> Byte_frame = Object.get_read_frame(Xcom_API._info_battery_voltage) >>> Byte_frame bytearray(b'\\xAA\\x00\\x01\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x0A\\x00\\x6F\\x71\\x00\\x01\\x01\\x00\\xB8\\x0B\\x00\\x00\\x01\\x00\\xC5\\x90') """ try: # Read Object-ID infomations from Dictionary and store it into a Buffer. Buffer = Xcom_API.__para_info_dict[object_id] # Return a Byte-Frame from the Read-Extenion method. return self.get_read_frame_ext(Buffer[0],object_id,Buffer[1]) except KeyError: # If the Object_ID is unknown, the method raise a ValueError. raise ValueError('Object_ID unknown!') # This method is used to generate a Byte-Frame for a 'write'-instruction. It can only be used with a known Object_ID, otherwise it # will raise a Value_Error. It Returns a bytearray. def get_write_frame(self, object_id, property_data): """ ### Description: This method is used to generate a Byte-Frame for a 'write'-instruction. It can only be used with a known Object_ID, otherwise it will raise a Value_Error. It Returns a bytearray. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument indicates, which id should be written. The following shows the possibilities: +-+-+ | | | +=+=+ | _para_maximum_current_of_ac_source | = 1107 | +-+-+ | _para_battery_charge_current | = 1138 | +-+-+ | _para_smart_boost_allowed | = 1126 | +-+-+ | _para_inverter_allowed | = 1124 | +-+-+ | _para_type_of_detection_of_grid_loss | = 1552 | +-+-+ | _para_charger_allowed | = 1125 | +-+-+ | _para_charger_uses_only_power_from_ac | = 1646 | +-+-+ | _para_ac_output_voltage | = 1286 | +-+-+ | _para_inverter_frequency | = 1112 | +-+-+ | _para_transfer_relay_allowed | = 1128 | +-+-+ | _para_limitation_of_the_power_boost | = 1607 | +-+-+ | _para_remote_entry_active | = 1545 | +-+-+ The **** argument is the value you want to send. ### Return-Value: This Method return a **bytearray** of the frame. ### Example-Code: ```>>> Object = Xcom_API() >>> Data_in_Ampere = 32.0 >>> Byte_frame = Object.get_write_frame(Xcom_API._para_maximum_current_of_ac_source, Data_in_Ampere) >>> Byte_frame bytearray(b'\\xAA\\x00\\x01\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x0E\\x00\\x73\\x79\\x00\\x02\\x02\\x00\\x53\\x04\\x00\\x00\\x05\\x00\\x00\\x00\\x00\\x42\\xA1\\xE6') """ try: # Read Object-ID infomations from Dictionary and store it into a Buffer. Buffer = Xcom_API.__para_info_dict[object_id] # Cheack for parameter if not Buffer[0] == Xcom_API._object_type_parameter: raise KeyError # Return a Byte-Frame from the Read-Extenion method. return self.get_write_frame_ext(Buffer[0],object_id,Buffer[1],property_data,Buffer[2]) except KeyError: # If the Object_ID is unknown, the method raise a ValueError. raise ValueError('Object_ID unknown!') # This method is used to decode Data from the received Byte-Frame, what you get from the xtender-system. It can only be used with a known Object_ID, # otherwise it will raise a Value_Error. It returns a List with the result of the returned Data and the Data itself. def get_data_from_frame(self, bytearray_of_frame): """ ### Description: This method is used to decode Data from the received Byte-Frame, what you get from the xtender-system. If CRC-Check is active, it will check the byte-frame and raises a Value_Error, if a CRC-Error was detected. It can only be used with a known Object_ID, otherwise it will raise a Value_Error. It returns a List with the result of the returned Data and the Data itself. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument is the frame what you get, when you receive data from the serial port. ### Return-Value: This Method return a **list** with two elements. The First is a **boolean** value, which is **True**, if the xtender-system detects an error and the second element of the **list** then contains the error-id. If no error occures, then the first element is **False** und the second element contains the answer of your request. The returned value of the second element is a **string**, **int** or **float**, depending of the service (read/write) of the request frame and of the **** argument. ### Example-Code: ```>>> Object = Xcom_API() >>> Frame = bytearray(b'\\xAA\\x37\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\xAA\\x66\\x02\\x01\\x01\\x00\\xB8\\x0B\\x00\\x00\\x01\\x00\\x00\\x40\\x42\\x42\\x8B\\x46') >>> Answer = Object.get_data_from_frame(Frame) >>> Answer [False, 48.5625] """ try: # Do a Frame-Check. If it fails, it returns a ValueError. if Xcom_API.__frame_check(self, bytearray_of_frame): self.__frame_check_done = True else: raise ValueError('Frame-Check failed!') # Read Object-ID infomations from Dictionary and store it into a Buffer. Buffer = Xcom_API.__para_info_dict[Xcom_API.__byte_frame_to_value(bytearray_of_frame[18:22],Xcom_API._format_int32)] # Return a Byte-Frame from the Read-Extension method. return self.get_data_from_frame_ext(bytearray_of_frame,Buffer[2]) except KeyError: # If the Object_ID is unknown, the method raise a ValueError. self.__frame_check_done = False raise ValueError('Object_ID unknown!') # This method is used to return the frame-flags as a binary. def get_bin_from_frame_flags(self, bytearray_of_frame): """ ### Description: This method is used to return the frame-flags as a binary. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument is the frame what you get, when you receive data from the serial port. ### Return-Value: The returned value is type **binary**. ### Example-Code: ```>>> Object = Xcom_API() >>> Frame = bytearray(b'\\xAA\\x37\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\xAA\\x66\\x02\\x01\\x01\\x00\\xB8\\x0B\\x00\\x00\\x01\\x00\\x00\\x40\\x42\\x42\\x8B\\x46') >>> Answer = Object.get_bin_from_frame_flags(Frame) >>> Answer 0b00110111 """ # Do a Frame-Check. If it fails, it returns a ValueError. if not Xcom_API.__frame_check(self, bytearray_of_frame): raise ValueError('Frame-Check failed!') # Create a Buffer with binary Information. Buffer = bin(bytearray_of_frame[1]) # Extend Buffer to 8-bit for i in range(0,10-len(Buffer)): Buffer = Buffer[0:2] + '0' + Buffer[2:] # Return Buffer. return Buffer # This method is used to return the frame-flags as a list with explanation (string) for each bit, starting from LSB to MSB. def get_text_from_frame_flags(self, bytearray_of_frame): """ ### Description: This method is used to return the frame-flags as a **list** with explanation (**string**) for each bit, starting with lsb. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument is the frame what you get, when you receive data from the serial port. ### Return-Value: The method returns a **list** with **strings** of explanation for each bit of the frame-flags, starting from LSB to MSB. ### Example-Code: ```>>> Object = Xcom_API() >>> Frame = bytearray(b'\\xAA\\x37\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\xAA\\x66\\x02\\x01\\x01\\x00\\xB8\\x0B\\x00\\x00\\x01\\x00\\x00\\x40\\x42\\x42\\x8B\\x46') >>> Answer = Object.get_text_from_frame_flags(Frame) >>> Answer ['Messages are pending.','A reset or restart was carried out.','The SD-Card is present.','The SD-Card is not full.','New datalog file on the SD-Card.','Datalogger is supported.'] """ # Create Buffer and get binary from byte-frame. Buffer = self.get_bin_from_frame_flags(bytearray_of_frame) # Create Out-Buffer. BufferOut = [] # Check bit 0. if int(Buffer[9]) == 1: BufferOut.append('Messages are pending.') else: BufferOut.append('No messages are pending.') # Check bit 1. if int(Buffer[8]) == 1: BufferOut.append('A reset or restart was carried out.') else: BufferOut.append('No reset or restart was carried out.') # Check bit 2. if int(Buffer[7]) == 1: BufferOut.append('The SD-Card is present.') else: BufferOut.append('No SD-Card is present') # Check bit 3. if int(Buffer[6]) == 1: BufferOut.append('The SD-Card is full.') else: BufferOut.append('The SD-Card is not full.') # Check bit 4. if int(Buffer[5]) == 1: BufferOut.append('New datalog file on the SD-Card.') else: BufferOut.append('No new datalog file on the SD-Card.') # Check bit 5. if int(Buffer[4]) == 1: BufferOut.append('Datalogger is supported.') else: BufferOut.append('Datalogger is not supported.') # Return list with Frame-flag informations. return BufferOut # This method is used to get the error message from the error-id. def get_text_from_error_id(self, error_id): """ ### Description: This method is used to get the error message from the error-id. ### Arguments: The **** argument is pointing to the object, which calls the method. The **** argument is an **int** from the xtender-system. ### Return-Value: The method returns a **strings** with the explanation of the error-id. ### Example-Code: ```>>> Object = Xcom_API() >>> Error_ID = 0x0022 >>> Answer = Object.get_text_from_error_id(Error_ID) >>> Answer 'OBJECT_ID_NOT_FOUND' """ try: # Check ID and store error-message in Buffer Buffer = Xcom_API.__error_code_dict[error_id] # return error-message return Buffer except KeyError: # If Error ID is unknown return 'Error-ID is unknown.'