def handle_request(self, request): if request.method != 'POST': return HttpResponse('Only POST with JSON body is allowed', status=405) serialised_request = serialize_django_request(request) ok, body = decode_json_body(serialised_request['request.body']) if ok is False: return HttpResponse(f'JSON ERROR: {body}', status=400, content_type='text/plain') try: devid = body['deviceId'] except KeyError as err: return HttpResponse( f'Invalid request payload, no devid found: {body}', status=400, content_type='text/plain') serialised_request['devid'] = devid serialised_request['time'] = datetime.datetime.utcnow().isoformat( ) + 'Z' message = data_pack(serialised_request) key = create_routing_key('ruuvistation', devid) send_message(settings.RAW_HTTP_EXCHANGE, key, message) if devid is not None: datalogger, created = get_datalogger(devid=devid, update_activity=True) return HttpResponse('OK', content_type='text/plain')
def handle_request(self, request): """ Decode json body and send serialised request to RabbitMQ exchange :param request: Django HttpRequest :return: HttpResponse """ if request.method != 'POST': return HttpResponse('Only POST with JSON body is allowed', status=405) serialised_request = serialize_django_request(request) ok, body = decode_json_body(serialised_request['request.body']) if ok is False: err_msg = f'JSON ERROR: {body}' logging.error(err_msg) # Error or warning? return HttpResponse(err_msg, status=400, content_type='text/plain') # TODO: this will fail if json is malformed devid = body['meta'].get('device', 'unknown') serialised_request['devid'] = devid serialised_request['time'] = datetime.datetime.utcnow().isoformat( ) + 'Z' logging.debug(json.dumps(body)) message = data_pack(serialised_request) key = create_routing_key('everynet', devid) send_message(settings.RAW_HTTP_EXCHANGE, key, message) if body.get('type') == 'uplink': datalogger, created = get_datalogger(devid=devid, update_activity=True) if created: logging.info(f'Created new Datalogger {devid}') return HttpResponse('OK', content_type='text/plain')
def parse_ruuvistation_request(serialised_request, data): # TODO: This may fail, so prepare to handle exception properly devid = data['deviceId'] datalogger, created = get_datalogger(devid=devid, update_activity=False) # Test it by configuring wrong decoder for some Datalogger try: parsed_data = decode_payload(datalogger, data, '') # print(json.dumps(parsed_data, indent=1)) except ValueError as err: decoder = get_datalogger_decoder(datalogger) err_msg = f'Failed to parse "{data}" using "{decoder}" for "{devid}": {err}' logger.warning(err_msg) # print(err_msg) serialised_request['parse_fail'] = { 'error_message': str(err), 'decoder': get_datalogger_decoder(datalogger) } save_parse_fail_datalogger_message(devid, data_pack(serialised_request)) return True logging.debug(parsed_data) # RabbitMQ part exchange = settings.PARSED_DATA_HEADERS_EXCHANGE # If gateway object is in parsed_data it should be looped also here for tag in parsed_data['ruuvitags']: # + [parsed_data['gateway']]: devid = tag['devid'] key = create_routing_key('ruuvistation', devid) datalines = tag['datalines'] message = create_parsed_data_message(devid, datalines=datalines) packed_message = data_pack(message) logger.debug( f'exchange={settings.PARSED_DATA_EXCHANGE} key={key} packed_message={packed_message}' ) config = {} # TODO: implement and use get_datalogger_config() if datalogger.application: config = json.loads(datalogger.application.config) # TODO: get influxdb variables from Application / Datalogger / Forward etc config if 'influxdb_database' in config and 'influxdb_measurement' in config: config['influxdb'] = '1' # Use key name as measurement's name, this will override what is set elsewhere # (e.g. in Application config), check save2influxdb.py # config['influxdb_measurement'] = 'ruuvitag' send_message(exchange, '', packed_message, headers=config) return True
def send_to_exchange(devid, datalogger, datalines, override_measurement=None): # RabbitMQ part key = create_routing_key('thingpark', devid) message = create_parsed_data_message(devid, datalines=datalines) packed_message = data_pack(message) exchange = settings.PARSED_DATA_HEADERS_EXCHANGE logger.debug(f'exchange={settings.PARSED_DATA_EXCHANGE} key={key} packed_message={packed_message}') config = {} # TODO: implement and use get_datalogger_config() if datalogger.application: config = json.loads(datalogger.application.config) # TODO: get influxdb variables from Application / Datalogger / Forward etc config if 'influxdb_database' in config and 'influxdb_measurement' in config: config['influxdb'] = '1' if override_measurement is not None: config['influxdb_measurement'] = override_measurement send_message(exchange, '', packed_message, headers=config)
def parse_thingpark_request(serialised_request, data): d = data['DevEUI_uplink'] devid = d['DevEUI'] port = d['FPort'] datalogger, created = get_datalogger(devid=devid, update_activity=False) timestamp = parse(d['Time']) timestamp = timestamp.astimezone(pytz.UTC) payload_hex = d['payload_hex'] rssi = d['LrrRSSI'] # TODO: This may fail, so prepare to handle exception properly # Test it by configuring wrong decoder for some Datalogger try: payload = decode_payload(datalogger, payload_hex, port, serialised_request=serialised_request) except ValueError as err: decoder = get_datalogger_decoder(datalogger) err_msg = f'Failed to parse "{payload_hex}" using "{decoder}" for "{devid}": {err}' logger.warning(err_msg) serialised_request['parse_fail'] = { 'error_message': str(err), 'decoder': get_datalogger_decoder(datalogger) } save_parse_fail_datalogger_message(devid, data_pack(serialised_request)) return True logging.debug(payload) # Some sensors may already return a dict of lists of datalines if not payload: return True if isinstance(payload, dict) and isinstance(payload[list(payload.keys())[0]], dict): parsed_data = payload for k in parsed_data.keys(): datalines = parsed_data[k]['datalines'] if len(datalines) > 0: datalines[-1]['data']['rssi'] = float(rssi) # Add rssi value to the latest dataline send_to_exchange(devid, datalogger, datalines, override_measurement=k) else: # Some sensors may already return a list of datalines if isinstance(payload, list): datalines = payload # Use payload as datalines (which already have timestamps) else: dataline = create_dataline(timestamp, payload) # Create dataline from LoRaWAN timestamp and payload datalines = [dataline] datalines[-1]['data']['rssi'] = float(rssi) # Add rssi value to the latest dataline send_to_exchange(devid, datalogger, datalines) return True
def parse_everynet_request(serialised_request, body): # FIXME: currently this parses only payload_hex from PAXCOUNTER. MUST check BKS too and others # TODO: create utility function, which extracts all interesting fields (needed here) out from request data devid = body['meta'].get('device', 'unknown') payload_base64 = body['params']['payload'] payload_hex = binascii.hexlify(base64.b64decode(payload_base64)) port = body['params']['port'] rssi = body['params']['radio']['hardware']['rssi'] timestamp = datetime.datetime.utcfromtimestamp(body['params']['rx_time']) timestamp = timestamp.astimezone(pytz.UTC) # TODO: this may fail if database is offline datalogger, created = get_datalogger(devid=devid, update_activity=False) payload = decode_payload(datalogger, payload_hex, port) payload['rssi'] = rssi # RabbitMQ part key = create_routing_key('everynet', devid) dataline = create_dataline(timestamp, payload) datalines = [dataline] message = create_parsed_data_message(devid, datalines=datalines) packed_message = data_pack(message) send_message(settings.PARSED_DATA_EXCHANGE, key, packed_message) return True