示例#1
0
def my_process(data):
    global opts

    ev = aiobs.HCI_Event()
    xx = ev.decode(data)
    if opts.mac:
        goon = False
        mac = ev.retrieve("peer")
        for x in mac:
            if x.val in opts.mac:
                goon = True
                break
        if not goon:
            return

    if opts.raw:
        print("Raw data: {}".format(ev.raw_data))
    if opts.eddy:
        xx = EddyStone().decode(ev)
        if xx:
            print("Google Beacon {}".format(xx))
    elif opts.ruuvi:
        xx = RuuviWeather().decode(ev)
        if xx:
            print("Weather info {}".format(xx))
    elif opts.bbw200:
        xx = Bbw200().decode(ev)
        if xx:
            print("{}".format(xx))
    else:
        ev.show(0)
示例#2
0
def my_process(data):
    global opts

    ev = aiobs.HCI_Event()
    xx = ev.decode(data)
    if opts.mac:
        goon = False
        mac = ev.retrieve("peer")
        for x in mac:
            if x.val in opts.mac:
                goon = True
                break
        if not goon:
            return

    if opts.raw:
        print("Raw data: {}".format(ev.raw_data))
    if opts.eddy:
        xx = EddyStone().decode(ev)
        if xx:
            print("Google Beacon {}".format(xx))
    elif opts.ruuvi:
        xx = RuuviWeather().decode(ev)
        if xx:
            print("Weather info {}".format(xx))
    elif opts.pebble:
        xx = BlueMaestro().decode(ev)
        if xx:
            print("Pebble info {}".format(xx))
    elif opts.atcmi:
        xx = ATCMiThermometer().decode(ev)
        if xx:
            print("Temperature info {}".format(xx))
    else:
        ev.show(0)
def _process_packet(data):
    global steps
    ev = aiobs.HCI_Event()  # event is passed to HCI in between the BT controller and host stack
    xx = ev.decode(data)  # decode the signal
    xx = EddyStone().decode(ev)  # run our decoded packet through the EddyStone protocol
    if xx:
        match = re.match(r'.*(group3p).*steps=(\d*).*', xx['url'])
        # if we match, we parse the steps and publish it to the broker
        if match:
            print('Received Eddystone Beacon Steps. Processing...')
            today = datetime.datetime.now().date()
            steps = int(match.groups()[1])
            print('Publishing steps...')
            client.publish(publish_topic, _determine_goal_met())
            with open(history_file, 'r') as inf:
                lines = inf.read().strip()
            with open(history_file, 'w') as outf:
                new_entry = '{},{},{}'.format(str(today),steps,goal)
                found = False
                for line in lines.split('\n'):
                    line = line.strip()
                    entry = [item.strip() for item in line.split(',')]
                    if entry[0] == str(today):
                        found = True
                        outf.write(new_entry)
                    else:
                        outf.write(line)
                    outf.write('\n')

                if not found:
                    outf.write(new_entry)
示例#4
0
    def run(self, data, simulator=0):
        if simulator > 0:
            # data is from BLE simulator, just return the data
            return data

        base_mesg = {"decoder": "none"}
        ev = aiobs.HCI_Event()
        ev.decode(data)
        mesg = packet_info(ev)
        if "mac" not in mesg:  # invalid packet if no mac (peer) address
            print("Decoder: invalid message, no mac.")
            return base_mesg

        decoders = self.get_decoders(mesg["mac"])
        if not decoders:
            return base_mesg

        # Try actually decode the message
        for decoder in decoders:
            func = self.all_decoders.get(decoder, None)
            if func:
                mesg.update(func(ev))
            if mesg:
                mesg["decoder"] = decoder
                return {**base_mesg, **mesg}

        return base_mesg
示例#5
0
    def parsingProcess(self, data):
        ev = aioblescan.HCI_Event()
        xx = ev.decode(data)

        hasAdvertisement = self.dataParser(ev)
        if hasAdvertisement and self.delegate is not None:
            self.delegate.handleDiscovery(ev)
示例#6
0
 def my_process(data):
     ev = aiobs.HCI_Event()
     xx = ev.decode(data)
     try:
         mac = ev.retrieve("peer")[0].val
     except:
         return
     if str(mac).find("b0:01:02") != -1:
         _LOGGER.debug("SEE 1 tariff counter")
         manufacturer_data = ev.retrieve("Manufacturer Specific Data")
         payload = manufacturer_data[0].payload
         payload = payload[1].val
         c_num = int.from_bytes(payload[6:8], byteorder="little")
         c_count = int.from_bytes(payload[9:12], byteorder="little")
         if measurement == "m3":
             inf[c_num] = c_count / 10000
         else:
             inf[c_num] = c_count / 10
     if (str(mac).find("b0:03:02") != -1) or (str(mac).find("b0:04:02") != -1):
         _LOGGER.debug("SEE 2 tariff counter")
         manufacturer_data = ev.retrieve("Manufacturer Specific Data")
         payload = manufacturer_data[0].payload
         payload = payload[1].val
         c_num = int.from_bytes(payload[6:8], byteorder="little")
         if str(mac).find("b0:03:02") != -1:
             c_num = str(c_num) + "_1"
         else:
             c_num = str(c_num) + "_2"
         c_count = int.from_bytes(payload[9:12], byteorder="little")
         c_temp = int.from_bytes(payload[14:16], byteorder="little") / 100
         inf[c_num.split("_")[0]] = c_temp
         if measurement == "m3":
             inf[c_num] = c_count / 10000
         else:
             inf[c_num] = c_count / 10
示例#7
0
def my_process(self):
    ev=aiobs.HCI_Event()
    xx=ev.decode(self)
    xx=EddyStone().decode(ev)
    if xx:
        print(xx)
        return (xx.get('rssi'))
示例#8
0
def _process_packet(data):
    ev = aiobs.HCI_Event()
    xx = ev.decode(data)
    xx = EddyStone().decode(ev)
    if xx:
        if xx['url'][:24] == 'https://wilshireliu.com/':
            #print("Google beacon: {}".format(xx['url'][:24]))
            print("Steps: {}".format(xx['url'][24:]))
            global actualsteps
            actualsteps = int(xx['url'][24:]) * 1000
            print("actual steps:", actualsteps)
            print("today goal:", today_goal)
            print("tomorrow goal:", tmr_goal)
            client = mqtt.Client("P1")
            client.on_message = on_message
            client.connect(broker_address)
            client.loop_start()
            client.subscribe(subscribetopic)
            time.sleep(3)
            if actualsteps < today_goal:
                message_goal = "GET UP!"
                client.publish(
                    publishtopic,
                    "Friend, the world is so beautiful so go outside and explore the world. Btw the goal of tomorrow is {}"
                    .format(tmr_goal))
                client.loop_stop()
            if actualsteps >= today_goal:
                message_goal = "GREAT JOB! You beat your goal"
                print(today_goal)
                client.publish(
                    publishtopic,
                    "Nice! You beat the goal and the goal of tomorrow is {}".
                    format(tmr_goal))
                client.loop_stop()
示例#9
0
def _process_packet(data):
    ev = aiobs.HCI_Event()
    xx = ev.decode(data)
    xx = EddyStone().decode(ev)
    if xx:
        if xx['url'][:24] == 'https://wilshireliu.com/':
            #print("Google beacon: {}".format(xx['url'][:24]))
            print("Steps: {}".format(xx['url'][24:]))
            global actualsteps
            actualsteps = int(xx['url'][24:])  #*1000
            print("actual steps:", actualsteps)
            print("today goal:", today_goal)
            print("tomorrow goal:", tmr_goal)
            client = mqtt.Client("P1")
            client.on_message = on_message
            client.connect(broker_address)
            client.loop_start()
            client.subscribe(subscribetopic)
            time.sleep(3)
            if actualsteps < today_goal:
                #client.publish(publishtopic, "You have only walked " + str(actualsteps) + " steps. You need to walk " + str(today_goal) + " steps.")
                client.publish(
                    publishtopic, "{\"actualSteps\": " + str(actualsteps) +
                    ", \"todayGoal\": " + str(today_goal) + "}")
                client.loop_stop()
            if actualsteps >= today_goal:
                print(today_goal)
                client.publish(
                    publishtopic,
                    "Nice! You beat the goal for today and the goal for tomorrow is {}"
                    .format(tmr_goal))
                client.loop_stop()
示例#10
0
def my_process(data):
    global opts

    ev=aiobs.HCI_Event()
    xx=ev.decode(data)
    
    # print(ev.__dict__)
    #print(ev.raw_data)
    file.write(ev.raw_data)
示例#11
0
def _process_packet(data):
    ev = aiobs.HCI_Event()
    xx = ev.decode(data)
    xx = EddyStone().decode(ev)
    if xx:
        if "team8" in xx['url']:
            value = format(xx['url'][13:21])
            print(value)
            print("sending pulication")
            client.publish(publishtopic, value)
示例#12
0
def processBLEBeacon(data):
    # While I'm not a fan of globals, not sure how else we can store state here easily
    global opts
    global reload_objects_at
    global tilts
    global uuids

    ev = aiobs.HCI_Event()
    xx = ev.decode(data)

    # To make things easier, let's convert the byte string to a hex string first
    raw_data_hex = ev.raw_data.hex()

    if len(raw_data_hex) < 80:  # Very quick filter to determine if this is a valid Tilt device
        return False
    if "1370f02d74de" not in raw_data_hex:  # Another very quick filter (honestly, might not be faster than just looking at uuid below)
        return False

    # For testing/viewing raw announcements, uncomment the following
    # print("Raw data (hex) {}: {}".format(len(raw_data_hex), raw_data_hex))
    # ev.show(0)

    try:
        # Let's use some of the functions of aioblesscan to tease out the mfg_specific_data payload
        payload = ev.retrieve("Payload for mfg_specific_data")[0].val.hex()

        # ...and then dissect said payload into a UUID, temp, gravity, and rssi (which isn't actually rssi)
        uuid = payload[8:40]
        temp = int.from_bytes(bytes.fromhex(payload[40:44]), byteorder='big')
        gravity = int.from_bytes(bytes.fromhex(payload[44:48]), byteorder='big')
        # tx_pwr = int.from_bytes(bytes.fromhex(payload[48:49]), byteorder='big')
        # rssi = int.from_bytes(bytes.fromhex(payload[49:50]), byteorder='big')
        rssi = 0  # TODO - Fix this
    except:
        return

    if verbose:
        print("Tilt Payload (hex): {}".format(payload))

    color = TiltHydrometer.color_lookup(uuid)  # Map the uuid back to our TiltHydrometer object
    tilts[color].process_decoded_values(gravity, temp, rssi)  # Process the data sent from the Tilt

    # The Fermentrack specific stuff:
    reload = False
    if datetime.datetime.now() > reload_objects_at:
        # Doing this so that as time passes while we're polling objects, we still end up reloading everything
        reload = True
        reload_objects_at = datetime.datetime.now() + datetime.timedelta(seconds=15)

    for this_tilt in tilts:
        if tilts[this_tilt].should_save():
            tilts[this_tilt].save_value_to_fermentrack(verbose=verbose)

        if reload:  # Users editing/changing objects in Fermentrack doesn't signal this process so reload on a timer
            tilts[this_tilt].load_obj_from_fermentrack()
示例#13
0
def test_some_packets(data, mac, temp, humidity, battery, battery_volts,
                      counter, rssi):
    ev = aiobs.HCI_Event()
    ev.decode(data)
    xx = ATCMiThermometer().decode(ev)
    assert mac == xx["mac address"], "Wrong MAC addr"
    assert temp == xx["temperature"], "Wrong temperature C"
    assert humidity == xx["humidity"], "Wrong humidity %"
    assert battery == xx["battery"], "Wrong battery %"
    assert battery_volts == xx["battery_volts"], "Wrong battery V"
    assert counter == xx["counter"], "Wrong counter"
    assert rssi == xx["rssi"], "Wrong rssi"
示例#14
0
def _process_packet(data):
    ev=aiobs.HCI_Event()
    xx = ev.decode(data)
    xx = EddyStone().decode(ev)
    if xx:
        if (xx['url'][:24] == "https://IoTexasSteps.com"):
                numOfSteps = xx['url'][25:]
                stepMessage = "IoTaxes steps:{}".format(numOfSteps)
                saveSteps(numOfSteps)
                print(stepMessage)
                event_loop.stop()
                btctrl.stop_scan_request()  
示例#15
0
def my_process(data):
    global current_x
    global current_y
    global direction
    global time_for_segment
    global current_segment_time
    global number_of_measurements_this_segment

    ev = aiobs.HCI_Event()
    xx = ev.decode(data)

    #print(xx)
    #return

    beacon = EddyStone().decode(ev)
    if beacon:
        #print("Google Beacon {}".format(beacon))
        rssi = beacon['rssi']
        #print(rssi)

        rssi_measurements[current_y][current_x] += rssi
        number_of_measurements_this_segment += 1
        # TODO SAVE RSSI

    if time.time() - current_segment_time > time_for_segment:

        print('MOVE')

        for i in range(0, 6):
            os.system('echo 1 >/sys/class/leds/led0/brightness')
            time.sleep(0.1)
            os.system('echo 0 >/sys/class/leds/led0/brightness')
            time.sleep(0.1)

        rssi_measurements[current_y][
            current_x] /= number_of_measurements_this_segment
        number_of_measurements_this_segment = 0

        current_x += direction
        if current_x == -1 or current_x == x_dim:
            current_x -= direction
            direction *= -1
            current_y += 1

        current_segment_time = time.time()

        print('moving to sector', current_x, current_y)

    if current_y == y_dim:
        print('ALL DONE')
        np.savetxt("foo.csv", rssi_measurements, delimiter=",")
        exit(0)
示例#16
0
def _process_packet(data):
  ev = aiobs.HCI_Event()
  xx = ev.decode(data)
  xx = EddyStone().decode(ev)
  if xx:
    if "http://cmd?" in xx['url']:
      global step_count
      step_count = xx['url'][11:]
      print(step_count)
      client.loop_start()
      client.subscribe(subscribetopic)
      time.sleep(4)
      client.loop_stop()
示例#17
0
 def my_process(data):
     ev = aiobs.HCI_Event()
     xx = ev.decode(data)
     xx = Tilt().decode(ev)
     if xx:
         # debug only, otherwise noisy
         # print("{}".format(xx))
         parsedData = json.loads(xx)
         color = get_tilt_color(parsedData['uuid'])
         # extra protection, if no color: that measured thing wasn't a tilt!
         if color:
             readings[parsedData['uuid']] = {'rssi': parsedData['rssi'], 'tx_power': parsedData['tx_power'], 'mac': parsedData['mac'],
                                             'major': parsedData['major'], 'minor': parsedData['minor'], 'uuid': parsedData['uuid'], 'color': color}
示例#18
0
 def test_eddystone_url(self):
     pckt = aioblescan.HCI_Event()
     pckt.decode(
         b'\x04>)\x02\x01\x03\x01\xdc)e\x90U\xf1\x1d\x02\x01\x06\x03\x03\xaa\xfe\x15\x16\xaa\xfe\x10\xf6\x03makecode\x00#about\xb5'
     )
     result = EddyStone().decode(pckt)
     self.assertDictEqual(
         result, {
             'mac address': 'f1:55:90:65:29:dc',
             'tx_power': -10,
             'url': 'https://makecode.com/#about',
             'rssi': -75
         })
示例#19
0
 def test_eddystone_uid(self):
     pckt = aioblescan.HCI_Event()
     pckt.decode(
         b'\x04>)\x02\x01\x03\x01\xdc)e\x90U\xf1\x1d\x02\x01\x06\x03\x03\xaa\xfe\x15\x16\xaa\xfe\x00\xf6\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x00\x00\x00\x00\x00X\xb6'
     )
     result = EddyStone().decode(pckt)
     self.assertDictEqual(
         result, {
             'tx_power': -10,
             'rssi': -74,
             'name space': (0x63).to_bytes(10, byteorder="big"),
             'instance': (0x58).to_bytes(6, byteorder="big"),
             'mac address': 'f1:55:90:65:29:dc'
         })
示例#20
0
def process(data):
    ev=aioblescan.HCI_Event()
    xx=ev.decode(data)
    sensor_mac = ev.retrieve("peer")
       
    xx=Fluke().decode(ev)
    
    if xx:
        #send results via MQTT
        mqtt.publish(TOPIC_PREFIX + "/" +sensor_mac[0].val+  "/data", json.dumps(xx))        
        pattern = '%s,sensor_mac=%s,sensor_type=%s,sensor_unit=%s,sensor_sw_version=%s,sensor_fw_version=%s sensor_value=%.'+str(xx['decimalPlaces'])+'f,sensor_rssi=%i,sensor_battery=%i,sensor_adv=%i %i'
        output = (pattern % (DEVICE_HOST,xx['mac'],xx['type'].replace(" ","\ "),xx['unit'].replace(" ","\ "),xx['sw_version'],xx['fw_version'],xx['value'],xx['rssi'],xx['battery'],xx['advertizing'],time.time()*1000000000))
        mqtt.publish(TOPIC_PREFIX_INFLUX + "/" +xx['mac']+  "/data", output)
        output = (pattern % (DEVICE_HOST+"_kafka",xx['mac'],xx['type'].replace(" ","\ "),xx['unit'].replace(" ","\ "),xx['sw_version'],xx['fw_version'],xx['value'],xx['rssi'],xx['battery'],xx['advertizing'],time.time()*1000000000))
        kafka_producer.publish(KAFKA_TOPIC,output)
示例#21
0
 def test_eddystone_url(self):
     pckt = aioblescan.HCI_Event()
     pckt.decode(
         b"\x04>)\x02\x01\x03\x01\xdc)e\x90U\xf1\x1d\x02\x01\x06\x03\x03\xaa\xfe\x15\x16\xaa\xfe\x10\xf6\x03makecode\x00#about\xb5"
     )
     result = EddyStone().decode(pckt)
     self.assertDictEqual(
         result,
         {
             "mac address": "f1:55:90:65:29:dc",
             "tx_power": -10,
             "url": "https://makecode.com/#about",
             "rssi": -75,
         },
     )
示例#22
0
    def ble_scan_callback(self, data):
        logger.debug("Received scan packet")

        # Get HCI event from data
        event = aiobs.HCI_Event()
        event.decode(data)

        # Don't bother with invalid packets
        if not self.is_valid_target(event):
            return

        # Create ThermoBeaconPacket instance to handle this event
        packet = ThermoBeaconPacket(self, event)
        if self.debug:
            self.packets.append(packet)
示例#23
0
def _process_packet(data):
    """
    Filter BLE packets to only show Eddystone data
    before calling packet_callback
    :param data: BLE Packet data
    :return: None
    """
    logging.debug('Packet found')
    ev = aioblescan.HCI_Event()
    ev.decode(data)
    logging.debug('Using the following callback %s', packet_callback)
    if packet_callback is not None:
        eddystone_data = aioblescan.EddyStone(ev)
        if eddystone_data:
            packet_callback(eddystone_data)
示例#24
0
 def test_eddystone_uid(self):
     pckt = aioblescan.HCI_Event()
     pckt.decode(
         b"\x04>)\x02\x01\x03\x01\xdc)e\x90U\xf1\x1d\x02\x01\x06\x03\x03\xaa\xfe\x15\x16\xaa\xfe\x00\xf6\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x00\x00\x00\x00\x00X\xb6"
     )
     result = EddyStone().decode(pckt)
     self.assertDictEqual(
         result,
         {
             "tx_power": -10,
             "rssi": -74,
             "name space": (0x63).to_bytes(10, byteorder="big"),
             "instance": (0x58).to_bytes(6, byteorder="big"),
             "mac address": "f1:55:90:65:29:dc",
         },
     )
示例#25
0
def _process_packet(data):
    #global val
    ev = aiobs.HCI_Event()
    xx = ev.decode(data)
    xx = EddyStone().decode(ev)

    if xx is not None:
        check = xx['url']
        if 'werock' in str(xx['url']):
            val = xx['url'][26:]
            print("{}".format(xx['url'][26:]))
            client.on_message = on_message
            client.loop_start()
            client.subscribe(subscribetopic)
            client.publish(publishtopic, val)
            #time.sleep(10)
            client.loop_stop()
示例#26
0
def parse_presence(data):
    hci_event = aiobs.HCI_Event()
    hci_event.decode(data)

    result = {"type": "presence"}
    mac = next(iter([x.val for x in hci_event.retrieve(aiobs.MACAddr)]), None)
    if mac:
        result["mac"] = mac
        rssi = next(
            iter([
                x.val for x in hci_event.retrieve(aiobs.IntByte)
                if x.name == 'rssi'
            ]), None)
        if rssi:
            result["rssi"] = rssi
        return result
    return None
示例#27
0
 def my_process(data):
     if time.time()-start >int(scan_duration):
         btctrl.stop_scan_request()
         conn.close()
         event_loop.stop()
     ev=aiobs.HCI_Event()
     xx = ev.decode(data)
     try:
         mac = ev.retrieve("peer")[0].val
     except:
         return
     if str(mac).find('b0:01:02') !=-1:
         manufacturer_data = ev.retrieve("Manufacturer Specific Data")   
         payload = manufacturer_data[0].payload
         payload = payload[1].val     
         c_num = int.from_bytes(payload[6:8], byteorder='little')
         c_count = int.from_bytes(payload[9:12], byteorder='little')
         inf[c_num] = c_count/10
示例#28
0
def my_process(data):
    global opts

    ev = aiobs.HCI_Event()
    xx = ev.decode(data)
    if opts.mac:
        goon = False
        mac = ev.retrieve("peer")
        for x in mac:
            if x.val in opts.mac:
                goon = True
                break
        if not goon:
            return

    if opts.raw:
        print("Raw data: {}".format(ev.raw_data))
    else:
        ev.show(0)
示例#29
0
def my_process(data):
    global opts
    ev = aiobs.HCI_Event()
    xx = ev.decode(data)
    try:
        mac = ev.retrieve("peer")
        for x in mac:
            gevonden_mac_adres = x.val
            #logger.debug(gevonden_mac_adres)
            #kijken of hij in de maclijst staat
            if gevonden_mac_adres in maclijst:
                #logger.debug("mac gevonden")
                #kijken of we hem al hebben gezien
                if gevonden_mac_adres in kanolijst:
                    #logger.debug("update datum in kanolijst")
                    #kijk wanneer laatste datum
                    #als laatste datum meer dan 10 min geleden doe schrijf_uitgeleend
                    TijdNu = datetime.now()
                    if wegtijd_verstreken(gevonden_mac_adres, UitleenMinimum):
                        logger.debug("meer dan 'uitleenminimum' geleden")
                        schrijf_uitgeleend(gevonden_mac_adres,
                                           kanolijst[gevonden_mac_adres],
                                           datetime.now())
                        #ook in live database schrijven dat de kano er weer is.
                    live_database_aanwezigheid(gevonden_mac_adres, "1")
                    #update laatste datum
                    kanolijst[gevonden_mac_adres] = TijdNu
                    logger.debug("entry update")
                    logger.debug(gevonden_mac_adres)
                    logger.debug(kanolijst[gevonden_mac_adres])
                else:
                    #voeg mac toe aan kanolijst met nu als laatste datum
                    logger.debug("voeg toe aan kanolijst")
                    kanolijst[gevonden_mac_adres] = datetime.now()
                    logger.debug("----")
                    logger.debug(kanolijst)
                    logger.debug("----")
                    #melden als aanwezig in lived database
                    live_database_aanwezigheid(gevonden_mac_adres, "1")
    except Exception as ex:
        logger.debug(ex)
示例#30
0
def processBLEBeacon(data):
    # While I'm not a fan of globals, not sure how else we can store state here easily
    global verbose
    global reload_objects_at
    global tilts

    ev = aiobs.HCI_Event()
    xx = ev.decode(data)

    # To make things easier, let's convert the byte string to a hex string first
    if ev.raw_data is None:
        if verbose:
            LOG.error("Event has no raw data")
        return False

    raw_data_hex = ev.raw_data.hex()

    if len(
            raw_data_hex
    ) < 80:  # Very quick filter to determine if this is a valid Tilt device
        # if verbose:
        #     LOG.info("Small raw_data_hex: {}".format(raw_data_hex))
        return False
    if "1370f02d74de" not in raw_data_hex:  # Another very quick filter (honestly, might not be faster than just looking at uuid below)
        # if verbose:
        #     LOG.info("Missing key in raw_data_hex: {}".format(raw_data_hex))
        return False

    # For testing/viewing raw announcements, uncomment the following
    # print("Raw data (hex) {}: {}".format(len(raw_data_hex), raw_data_hex))
    # ev.show(0)

    # try:
    #     mac_addr = ev.retrieve("peer")[0].val
    # except:
    #     pass

    try:
        # Let's use some of the functions of aioblesscan to tease out the mfg_specific_data payload

        manufacturer_data = ev.retrieve("Manufacturer Specific Data")
        payload = manufacturer_data[0].payload
        payload = payload[1].val.hex()

        # ...and then dissect said payload into a UUID, temp, and gravity
        uuid = payload[4:36]
        temp = int.from_bytes(bytes.fromhex(payload[36:40]), byteorder='big')
        gravity = int.from_bytes(bytes.fromhex(payload[40:44]),
                                 byteorder='big')
        # On the latest tilts, TX power is used for battery
        tx_pwr = int.from_bytes(bytes.fromhex(payload[44:46]),
                                byteorder='big',
                                signed=False)
        rssi = ev.retrieve("rssi")[-1].val

    except Exception as e:
        LOG.error(e)
        capture_exception(e)
        exit(1)
        return False  # This can't be called, but it's here to make Pycharm happy

    if verbose:
        LOG.info("Tilt Payload (hex): {}".format(raw_data_hex))

    color = TiltHydrometer.color_lookup(
        uuid)  # Map the uuid back to our TiltHydrometer object
    tilts[color].process_decoded_values(
        gravity, temp, rssi, tx_pwr)  # Process the data sent from the Tilt

    if verbose:
        # print("Color {} - MAC {}".format(color, mac_addr))
        print("Raw Data: `{}`".format(raw_data_hex))
        print(
            f"{color} - Temp: {temp}, Gravity: {gravity}, RSSI: {rssi}, TX Pwr: {tx_pwr}"
        )

    # The Fermentrack specific stuff:
    reload = False
    if datetime.datetime.now() > reload_objects_at:
        # Doing this so that as time passes while we're polling objects, we still end up reloading everything
        reload = True
        reload_objects_at = datetime.datetime.now() + datetime.timedelta(
            seconds=30)

    for this_tilt in tilts:
        if tilts[this_tilt].should_save():
            if verbose:
                LOG.info("Saving {} to Fermentrack".format(this_tilt))
            tilts[this_tilt].save_value_to_fermentrack(verbose=verbose)

        if reload:  # Users editing/changing objects in Fermentrack doesn't signal this process so reload on a timer
            if verbose:
                LOG.info("Loading {} from Fermentrack".format(this_tilt))
            tilts[this_tilt].load_obj_from_fermentrack()