示例#1
0
def test_decode_data_raises_checksum_exception():
    rscp = RSCPUtils()
    hex_ = "e3dc" + \
           "1010" + \
           int.to_bytes(1564732130, length=8, byteorder=sys.byteorder).hex() + \
           int.to_bytes(94967295, length=4, byteorder=sys.byteorder).hex() + \
           int.to_bytes(256, length=2, byteorder=sys.byteorder).hex() + \
           bytearray(256).hex()
    checksum = 156
    complete_hex = hex_ + int.to_bytes(
        checksum, length=4, byteorder=sys.byteorder).hex()
    with pytest.raises(RSCPFrameError):
        rscp.decode_data(bytes.fromhex(complete_hex))
示例#2
0
def test_decode_data_returns_correct_value():
    rscp = RSCPUtils()
    hex_ = "e3dc" + \
           "1010" + \
           int.to_bytes(1564732130, length=8, byteorder=sys.byteorder).hex() + \
           int.to_bytes(94967295, length=4, byteorder=sys.byteorder).hex() + \
           int.to_bytes(11, length=2, byteorder=sys.byteorder).hex() + \
           "01000001" + "07" + struct.pack("<H", 4).hex() + struct.pack("<I", 98562).hex()
    checksum = zlib.crc32(bytes.fromhex(hex_))
    complete_hex = hex_ + int.to_bytes(
        checksum, length=4, byteorder=sys.byteorder).hex()
    rscp_dto = rscp.decode_data(bytes.fromhex(complete_hex))
    assert rscp_dto.tag == RSCPTag(0x01000001)
    assert rscp_dto.type == RSCPType(0x07)
    assert rscp_dto.data == 98562
示例#3
0
def test_encrypted_frame_can_be_decrypted():
    encryptor = RSCPEncryptDecrypt("my_key")
    rscp_utils = RSCPUtils()
    encoded_data = rscp_utils.encode_data(
        RSCPDTO(RSCPTag.RSCP_REQ_AUTHENTICATION, RSCPType.Container, [
            RSCPDTO(RSCPTag.RSCP_AUTHENTICATION_USER, RSCPType.CString,
                    'username'),
            RSCPDTO(RSCPTag.RSCP_AUTHENTICATION_PASSWORD, RSCPType.CString,
                    'password')
        ]))
    framed_data = rscp_utils.encode_frame(encoded_data)
    encrypted_data = encryptor.encrypt(framed_data)
    decrypted_data = encryptor.decrypt(encrypted_data)
    redecoded_data = rscp_utils.decode_data(decrypted_data)
    assert redecoded_data.tag == RSCPTag.RSCP_REQ_AUTHENTICATION
    assert redecoded_data.type == RSCPType.Container
    assert len(redecoded_data.data) == 2
    assert redecoded_data.data[0].tag == RSCPTag.RSCP_AUTHENTICATION_USER
    assert redecoded_data.data[0].type == RSCPType.CString
    assert redecoded_data.data[0].data == 'username'
    assert redecoded_data.data[1].tag == RSCPTag.RSCP_AUTHENTICATION_PASSWORD
    assert redecoded_data.data[1].type == RSCPType.CString
    assert redecoded_data.data[1].data == 'password'
示例#4
0
class E3DC:
    PORT = 5033
    BUFFER_SIZE = 1024 * 32

    def __init__(self, username, password, ip, key):
        self.password = password
        self.username = username
        self.ip = ip
        self.socket = None
        self.key = key
        self.waittime = 0.01
        self.rscp_utils = RSCPUtils()

    def create_encrypt(self):
        self.encrypt_decrypt = RSCPEncryptDecrypt(self.key)

    def send_requests2(self,
                       payload: [Union[RSCPDTO, RSCPTag]],
                       waittime=0.0) -> [RSCPDTO]:
        """
        This function will send a list of requests consisting of RSCPDTO's oder RSCPTag's to the e3dc
        and returns a list of responses.

        i.e. responses = send_requests([RSCPTag.EMS_REQ_BAT_SOC, RSCPTag.EMS_REQ_POWER_PV,
                                            RSCPTag.EMS_REQ_POWER_BAT, RSCPTag.EMS_REQ_POWER_GRID,
                                            RSCPTag.EMS_REQ_POWER_WB_ALL])
        :param payload: A list of requests
        :return: A list of responses in form of RSCPDTO's
        """
        dto_list: [RSCPDTO] = []
        for payload_element in payload:
            if isinstance(payload_element, RSCPTag):
                dto_list.append(RSCPDTO(payload_element))
            else:
                dto_list.append(payload_element)
        logger.debug("Sending " + str(len(dto_list)) + " requests to " +
                     str(self.ip))
        responses: [RSCPDTO] = []
        dto: RSCPDTO
        for dto in dto_list:
            response = self.send_request(dto, True, waittime=waittime)
            responses.append(response)
        return responses

    def send_requests(self,
                      payload: [Union[RSCPDTO, RSCPTag]],
                      waittime=0.0) -> [RSCPDTO]:
        payload_all = bytes()
        for payload_element in payload:
            if isinstance(payload_element, RSCPTag):
                dto = RSCPDTO(payload_element)
            else:
                dto = payload_element

            payload_all += self.rscp_utils.encode_data(dto)

        prepared_data = self.rscp_utils.encode_frame(payload_all)
        response = self.send_request(prepared_data, True, waittime)

        responses: [RSCPDTO] = []
        if response.type == RSCPType.Container:
            data: RSCPDTO
            for data in response:
                responses.append(data)
        else:
            responses.append(response)

        return responses

    def send_request(self,
                     payload: Union[RSCPDTO, RSCPTag, bytes],
                     keep_connection_alive: bool = False,
                     waittime: float = 0.0) -> RSCPDTO:
        """
        This will perform a single request.

        :param payload: The payload that defines the request
        :param keep_connection_alive: A flag whether to keep the connection alive or not
        :return: A response object as RSCPDTO
        """
        if isinstance(payload, RSCPTag):
            payload = RSCPDTO(payload)
        if self.socket is None:
            self._connect()

        if isinstance(payload, bytes):
            prepared_data = payload
        else:
            encode_data = self.rscp_utils.encode_data(payload)
            prepared_data = self.rscp_utils.encode_frame(encode_data)

        #rawdata = binascii.hexlify(prepared_data)
        #logger.debug('Send RAW: ' + str(rawdata))
        logger.debug('Send ' + str(len(prepared_data)) + ' Bytes')
        encrypted_data = self.encrypt_decrypt.encrypt(prepared_data)
        try:
            self.socket.send(encrypted_data)
        except:
            self._disconnect()
            raise

        wait = self.waittime + waittime
        if wait > 0.0:
            time.sleep(wait)

        response = self._receive()
        if response.type == RSCPType.Error:
            logger.debug("Error type returned: " + str(response.data))
            raise (RSCPCommunicationError(
                'Error type returned: ' + str(response.data), logger,
                response))
        if not keep_connection_alive:
            self._disconnect()
        return response

    def _connect(self):
        if self.socket is None:
            logger.info("Trying to establish connection to " + str(self.ip))
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.connect((self.ip, self.PORT))
            self.socket.setblocking(False)
            rscp_dto = RSCPDTO(
                RSCPTag.RSCP_REQ_AUTHENTICATION, RSCPType.Container, [
                    RSCPDTO(RSCPTag.RSCP_AUTHENTICATION_USER, RSCPType.CString,
                            self.username),
                    RSCPDTO(RSCPTag.RSCP_AUTHENTICATION_PASSWORD,
                            RSCPType.CString, self.password)
                ], None)
            self.create_encrypt()
            result = self.send_request(rscp_dto, True)
            if result.type == RSCPType.Error:
                self._disconnect()
                raise RSCPAuthenticationError("Invalid username or password",
                                              logger)

    def _disconnect(self):
        logger.info("Closing connection to " + str(self.ip))
        self.socket.close()
        self.socket = None

    def _receive(self) -> RSCPDTO:
        logger.debug("Waiting for response from " + str(self.ip))
        decrypted_data = None
        wait = 0.01
        while not decrypted_data:
            try:
                data = self.socket.recv(self.BUFFER_SIZE)
                logger.debug('Received ' + str(len(data)) + ' Bytes')
                if len(data) == 0:
                    self.socket.close()
                    raise RSCPCommunicationError(
                        "Did not receive data from e3dc", logger)
                self.rscp_utils = RSCPUtils()

                decrypted_data = self.encrypt_decrypt.decrypt(data)
            except BlockingIOError:
                logger.debug('Keine Daten empfangen, warte ' + str(wait) + 's')
                time.sleep(wait)
                wait *= 2
                if wait > 2:
                    raise

        #rawdata = binascii.hexlify(decrypted_data)
        #logger.debug('Response RAW: ' + str(rawdata))
        rscp_dto = self.rscp_utils.decode_data(decrypted_data)
        logger.debug("Received DTO Type: " + rscp_dto.type.name +
                     ", DTO Tag: " + rscp_dto.tag.name)
        return rscp_dto
示例#5
0
class E3DC:
    PORT = 5033
    BUFFER_SIZE = 1024 * 32

    def __init__(self, username, password, ip, key):
        self.password = password
        self.username = username
        self.encrypt_decrypt = RSCPEncryptDecrypt(key)
        self.ip = ip
        self.socket = None
        self.rscp_utils = RSCPUtils()

    def send_requests(self, paylod: [Union[RSCPDTO, RSCPTag]]) -> [RSCPDTO]:
        """
        This function will send a list of requests consisting of RSCPDTO's oder RSCPTag's to the e3dc
        and returns a list of responses.

        i.e. responses = send_requests([RSCPTag.EMS_REQ_BAT_SOC, RSCPTag.EMS_REQ_POWER_PV,
                                            RSCPTag.EMS_REQ_POWER_BAT, RSCPTag.EMS_REQ_POWER_GRID,
                                            RSCPTag.EMS_REQ_POWER_WB_ALL])
        :param paylod: A list of requests
        :return: A list of responses in form of RSCPDTO's
        """
        dto_list: [RSCPDTO] = []
        for payload_element in paylod:
            if isinstance(payload_element, RSCPTag):
                dto_list.append(RSCPDTO(payload_element))
            else:
                dto_list.append(payload_element)
        logger.info("Sending " + str(len(dto_list)) + " requests to " +
                    str(self.ip))
        responses: [RSCPDTO] = []
        dto: RSCPDTO
        for dto in dto_list:
            responses.append(self.send_request(dto, True))
        return responses

    def send_request(self,
                     payload: Union[RSCPDTO, RSCPTag],
                     keep_connection_alive: bool = False) -> RSCPDTO:
        """
        This will perform a single request.

        :param payload: The payload that defines the request
        :param keep_connection_alive: A flag whether to keep the connection alive or not
        :return: A response object as RSCPDTO
        """
        if isinstance(payload, RSCPTag):
            payload = RSCPDTO(payload)
        if self.socket is None:
            self._connect()
        encode_data = self.rscp_utils.encode_data(payload)
        prepared_data = self.rscp_utils.encode_frame(encode_data)
        encrypted_data = self.encrypt_decrypt.encrypt(prepared_data)
        self.socket.send(encrypted_data)
        response = self._receive()
        if response.type == RSCPType.Error:
            logger.error("Error type returned")
            raise (RSCPCommunicationError(None, logger))
        if not keep_connection_alive:
            self._disconnect()
        return response

    def _connect(self):
        if self.socket is None:
            logger.info("Trying to establish connection to " + str(self.ip))
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.connect((self.ip, self.PORT))
            rscp_dto = RSCPDTO(
                RSCPTag.RSCP_REQ_AUTHENTICATION, RSCPType.Container, [
                    RSCPDTO(RSCPTag.RSCP_AUTHENTICATION_USER, RSCPType.CString,
                            self.username),
                    RSCPDTO(RSCPTag.RSCP_AUTHENTICATION_PASSWORD,
                            RSCPType.CString, self.password)
                ], None)
            result = self.send_request(rscp_dto, True)
            if result.type == RSCPType.Error:
                self._disconnect()
                raise RSCPAuthenticationError("Invalid username or password",
                                              logger)

    def _disconnect(self):
        logger.info("Closing connection to " + str(self.ip))
        self.socket.close()
        self.socket = None

    def _receive(self) -> RSCPDTO:
        logger.info("Waiting for response from " + str(self.ip))
        data = self.socket.recv(self.BUFFER_SIZE)
        if len(data) == 0:
            self.socket.close()
            raise RSCPCommunicationError("Did not receive data from e3dc",
                                         logger)
        self.rscp_utils = RSCPUtils()
        decrypted_data = self.encrypt_decrypt.decrypt(data)
        rscp_dto = self.rscp_utils.decode_data(decrypted_data)
        logger.debug("Received DTO Type: " + rscp_dto.type.name +
                     ", DTO Tag: " + rscp_dto.tag.name)
        return rscp_dto