예제 #1
0
    def _process_tuya(self, reply):

        a = BitArray(reply)

        for s in a.split('0x55aa', bytealigned=True):
            sbytes = s.tobytes()
            if sbytes[:2] == b'\x55\xaa':
                count = int.from_bytes(sbytes[3:4], byteorder='little')
                if self.request_cnt == count:
                    self.received_footer = True

                cmd = int.from_bytes(sbytes[9:10], byteorder='little')
                lendata = int.from_bytes(sbytes[13:14], byteorder='little')
                dataend = 14 + (lendata - 8)
                data = sbytes[18:dataend]
                if cmd == 7:
                    self._process_contole_result(data)
                elif cmd == 8:
                    self.results.append(self._process_status_result(data))
                elif cmd == 9:
                    self._process_heartbeat_result(data)
                elif cmd == 10:
                    self.results.append(self._process_query_result(data))
                elif cmd == 13:
                    self._process_contole_new_result(data)
                elif cmd == 16:
                    self._process_query_new_result(data)
예제 #2
0
def _process_raw_reply(device: dict, raw_reply: bytes):
    """
    Splits the raw reply(s) into chuncks and decrypts it.
    returns json str or str (error)
    """

    a = BitArray(raw_reply)

    #TODO: don't overwrite variables
    for s in a.split('0x000055aa', bytealigned=True):
        sbytes = s.tobytes()
        payload = None

        # Skip invalid messages
        if len(sbytes) < 28 or not s.endswith('0x0000aa55'):
            continue

        # Parse header
        seq = int.from_bytes(sbytes[4:8], byteorder='big')
        cmd = int.from_bytes(sbytes[8:12], byteorder='big')
        sz = int.from_bytes(sbytes[12:16], byteorder='big')
        rc = int.from_bytes(sbytes[16:20], byteorder='big')
        has_return_code = (rc & 0xFFFFFF00) == 0
        crc = int.from_bytes(sbytes[-8:-4], byteorder='big')

        # Check CRC
        if crc != binascii.crc32(sbytes[:-8]):
            continue

        if device['protocol'] == '3.1':

            data = sbytes[20:-8]
            if sbytes[20:21] == b'{':
                if not isinstance(data, str):
                    payload = data.decode()
            elif sbytes[20:23] == b'3.1':
                logger.info('we\'ve got a 3.1 reply, code untested')
                data = data[3:]  # remove version header
                data = data[
                    16:]  # remove (what I'm guessing, but not confirmed is) 16-bytes of MD5 hexdigest of payload
                payload = aescipher.decrypt(device['localkey'], data)

        elif device['protocol'] == '3.3':
            if sz > 12:
                data = sbytes[20:8 + sz]
                if cmd == tf.STATUS:
                    data = data[15:]
                payload = aescipher.decrypt(device['localkey'], data, False)

        msg = {"cmd": cmd, "seq": seq, "data": payload}
        if has_return_code:
            msg['rc'] = rc
        logger.debug(
            "(%s) received msg (seq %s): [%x:%s] rc: [%s] payload: [%s]",
            device["ip"], msg['seq'], msg['cmd'],
            tf.cmd_to_string.get(msg['cmd'],
                                 f'UNKNOWN'), rc if has_return_code else '-',
            msg.get('data', ''))
        yield msg
 def testByteAligned(self):
     bitstring.bytealigned = True
     a = BitArray("0x00 ff 0f f")
     l = list(a.findall("0xff"))
     self.assertEqual(l, [8])
     p = a.find("0x0f")[0]
     self.assertEqual(p, 16)
     p = a.rfind("0xff")[0]
     self.assertEqual(p, 8)
     s = list(a.split("0xff"))
     self.assertEqual(s, ["0x00", "0xff0ff"])
     a.replace("0xff", "")
     self.assertEqual(a, "0x000ff")
예제 #4
0
 def testByteAligned(self):
     bitstring.bytealigned = True
     a = BitArray('0x00 ff 0f f')
     l = list(a.findall('0xff'))
     self.assertEqual(l, [8])
     p = a.find('0x0f')[0]
     self.assertEqual(p, 16)
     p = a.rfind('0xff')[0]
     self.assertEqual(p, 8)
     s = list(a.split('0xff'))
     self.assertEqual(s, ['0x00', '0xff0ff'])
     a.replace('0xff', '')
     self.assertEqual(a, '0x000ff')
예제 #5
0
 def testByteAligned(self):
     bitstring.settings.bytealigned = True
     a = BitArray('0x00 ff 0f f')
     l = list(a.findall('0xff'))
     self.assertEqual(l, [8])
     p = a.find('0x0f')[0]
     self.assertEqual(p, 16)
     p = a.rfind('0xff')[0]
     self.assertEqual(p, 8)
     s = list(a.split('0xff'))
     self.assertEqual(s, ['0x00', '0xff0ff'])
     a.replace('0xff', '')
     self.assertEqual(a, '0x000ff')
예제 #6
0
 def testNotByteAligned(self):
     bitstring.bytealigned = False
     a = BitArray('0x00 ff 0f f')
     l = list(a.findall('0xff'))
     self.assertEqual(l, [8, 20])
     p = a.find('0x0f')[0]
     self.assertEqual(p, 4)
     p = a.rfind('0xff')[0]
     self.assertEqual(p, 20)
     s = list(a.split('0xff'))
     self.assertEqual(s, ['0x00', '0xff0', '0xff'])
     a.replace('0xff', '')
     self.assertEqual(a, '0x000')
예제 #7
0
 def testNotByteAligned(self):
     bitstring.settings.bytealigned = False
     a = BitArray('0x00 ff 0f f')
     l = list(a.findall('0xff'))
     self.assertEqual(l, [8, 20])
     p = a.find('0x0f')[0]
     self.assertEqual(p, 4)
     p = a.rfind('0xff')[0]
     self.assertEqual(p, 20)
     s = list(a.split('0xff'))
     self.assertEqual(s, ['0x00', '0xff0', '0xff'])
     a.replace('0xff', '')
     self.assertEqual(a, '0x000')
예제 #8
0
def _process_raw_reply(device: dict, raw_reply: bytes):       
   
    a = BitArray(raw_reply)    
    
    for s in a.split('0x000055aa', bytealigned=True):
        sbytes = s.tobytes()
        cmd = int.from_bytes(sbytes[11:12], byteorder='little')
        
        if cmd in [STATUS, DP_QUERY, DP_QUERY_NEW]:
            
            data = sbytes[20:8+int.from_bytes(sbytes[15:16], byteorder='little')]
            if cmd == STATUS:
                data = data[15:]
            yield aescipher.decrypt(device['localkey'], data, False)
예제 #9
0
 def testSplit(self):
     a = BitArray('0x4700004711472222')
     l = list(a.split('0x47', bytealigned=True))
     self.assertEqual(l, ['', '0x472222', '0x4711', '0x470000'])
예제 #10
0
    def parse(cls, address, data):
        '''Used to parse data received from RuuviTag.
        Currently supports versions 3 and 5 of the protocol.

        Arguments:
            address (str): MAC address of RuuviTag.
            data (bytes): received data in bytes.
        '''
        b = BitArray(bytes=data)

        # Try to find protocol version 3
        # https://github.com/ruuvi/ruuvi-sensor-protocols#data-format-3-protocol-specification
        if b.find('0xFF990403'):
            # If it's found, parse data
            b = list(b.split('0xFF990403', count=2))[-1]
            _, humidity, temperature_sign, temperature, temperature_fraction, pressure, \
                accel_x, accel_y, accel_z, battery_voltage = b.unpack(
                    # Ignore the packet type and manufacturer specs,
                    # as they've been handled before
                    'uint:32,' +
                    # Parse the actual payload
                    'uint:8, int:1, uint:7, uint:8, uint:16,' +
                    'int:16, int:16, int:16, uint:16')

            temperature_sign = -1 if temperature_sign == -1 else 1

            # ... and return an instance of the calling class
            return cls(address,
                       3,
                       temperature=temperature_sign *
                       (float(temperature) + temperature_fraction / 100.0),
                       humidity=humidity / 2.0,
                       pressure=(pressure + 50000) / 100.0,
                       acceleration_x=accel_x / 1000.0,
                       acceleration_y=accel_y / 1000.0,
                       acceleration_z=accel_z / 1000.0,
                       battery_voltage=battery_voltage / 1000.0)

        # Try to find protocol version 5
        # https://github.com/ruuvi/ruuvi-sensor-protocols#data-format-5-protocol-specification
        if b.find('0xFF990405'):
            # If it's found, parse data
            b = list(b.split('0xFF990405', count=2))[-1]
            _, temperature, humidity, pressure, \
                accel_x, accel_y, accel_z, \
                battery_voltage, tx_power, \
                movement_counter, measurement_sequence, mac = b.unpack(
                    # Ignore the packet type and manufacturer specs,
                    # as they've been handled before
                    'uint:32,' +
                    # Parse the actual payload
                    'int:16, uint:16, uint:16,' +
                    'int:16, int:16, int:16,' +
                    'uint:11, uint:5,' +
                    'uint:8, uint:16, uint:48')

            # Not sure what to do with MAC at the moment?
            # Maybe compare it to the one received by btle and
            # raise an exception if doesn't match?
            mac = '%x' % mac
            mac = ':'.join(mac[i:i + 2] for i in range(0, 12, 2))

            # ... and return an instance of the calling class
            return cls(address,
                       5,
                       temperature=float(temperature) * 0.005,
                       humidity=humidity * 0.0025,
                       pressure=(pressure + 50000) / 100.0,
                       acceleration_x=accel_x / 1000.0,
                       acceleration_y=accel_y / 1000.0,
                       acceleration_z=accel_z / 1000.0,
                       battery_voltage=(battery_voltage + 1600) / 1000.0,
                       tx_power=-40 + tx_power,
                       movement_counter=movement_counter,
                       measurement_sequence=measurement_sequence)
예제 #11
0
# originalfilename_1_GBA.vpk
# originalfilename_2_GC.vpk
CARD_TYPE = 'Animal Crossing'
# CARD_TYPE could be expanded to other cards later;
# only Animal Crossing is currently supported.
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + '/cards'
IN_DIR = BASE_DIR + '/bin/'
OUT_DIR = BASE_DIR + '/vpk/'
for file in os.listdir(IN_DIR):
    if file.endswith(".bin"):
        full_path = IN_DIR + file
        # https://pythonhosted.org/bitstring/creation.html#from-a-file
        a = BitArray(filename=full_path)
        # vpk0 = 76 70 6B 30. vpk0 is the header for each chunk of data we want
        for chunk_num, chunk in enumerate(
                a.split('0x76706B30', bytealigned=True)):
            card_extension = '.vpk'
            note = '_'
            # skip chunk 0 (when batch processing *.vpk files).
            # it's a header rather than a compressed VPK chunk
            if chunk_num == 0:
                card_extension = '.header'
            if CARD_TYPE == 'Animal Crossing':
                if chunk_num == 1:
                    # hunch, bigger because of graphics data
                    note = note + "GBA"
                if chunk_num == 2:
                    # almost certain, I've sent stuff to GC from this chunk
                    note = note + "GC"
            if note == '_':
                note = ''