def finalize_message(self, message): if "correlation_id" in message["body"]["headers"]: message["properties"]["correlation_id"] = message["body"][ "headers"]["correlation_id"] if "reply_to" in message["body"]["headers"]: message["properties"]["reply_to"] = message["body"]["headers"][ "reply_to"] # print("finalize message: %s" % message) message["body"] = msgpack.dumps(message["body"]) # Lets test if we can compress. Set headers as needed. if len(message["body"]) > 800: beforeZlib = len(message["body"]) message["body"] = zlib.compress( message["body"], 5 ) # 5 appears to be the best speed/compression ratio - MSchwenk afterZlib = len(message["body"]) message["meta"]["compression_percent"] = percentage( afterZlib, beforeZlib) message["properties"]["content_encoding"] = "zlib" else: message["properties"]["content_encoding"] = "text" message["meta"]["finalized_for_sending"] = True return message
def finalize_message(self, message): if 'correlation_id' in message['body']['headers']: message['properties']['correlation_id'] = message['body'][ 'headers']['correlation_id'] if 'reply_to' in message['body']['headers']: message['properties']['reply_to'] = message['body']['headers'][ 'reply_to'] # print("finalize message: %s" % message) message['body'] = msgpack.dumps(message['body']) # Lets test if we can compress. Set headers as needed. if len(message['body']) > 800: beforeZlib = len(message['body']) message['body'] = zlib.compress( message['body'], 5 ) # 5 appears to be the best speed/compression ratio - MSchwenk afterZlib = len(message['body']) message['meta']['compression_percent'] = percentage( afterZlib, beforeZlib) message['properties']['content_encoding'] = "zlib" else: message['properties']['content_encoding'] = 'text' # request_msg['meta']['content_encoding'] = request_msg['properties']['content_encoding'] # request_msg['meta']['payload_size'] = len(request_msg['body']) message['meta']['finalized_for_sending'] = True return message
def finalize_message(self, message: dict): if "correlation_id" in message["body"]["headers"]: message["properties"]["correlation_id"] = message["body"]["headers"]["correlation_id"] if "reply_correlation_id" in message["body"]["headers"]: message["properties"]["headers"]["reply_correlation_id"] = message["body"]["headers"]["reply_correlation_id"] message["body"] = msgpack.packb(message["body"]) # Lets test if we can compress. Set headers as needed. beforeZlib = len(message["body"]) if beforeZlib > 900: message["properties"]["content_type"] = "msgpack_zip" message["body"] = self._Tools.data_pickle(message["body"], "zip") message["meta"]["compression_percent"] = round(percentage(len(message["body"]), beforeZlib), 2) message["meta"]["finalized_for_sending"] = True return message
def generate_message(self, exchange_name, source, destination, header_type, headers, body, callback=None, correlation_id=None, message_id=None): """ When interacting with Yombo AMQP servers, we use a standard messaging layout. The below helps other functions and libraries conform to this standard. This only creates the message, it doesn't send it. Use the publish() function to complete that. **Usage**: .. code-block:: python requestData = { "exchange_name" : "gw_config", "source" : "yombo.gateway.lib.configurationupdate", "destination" : "yombo.server.configs", "callback" : self.amqp_direct_incoming, "body" : { "DataType" : "Object", "Request" : requestContent, }, } request = self.AMQPYombo.generateRequest(**requestData) :param exchange_name: The exchange the request should go to. :type exchange_name: str :param source: Value for the 'source' field. :type source: str :param destination: Value of the 'destination' field. :type destination: str :param header_type: Type of header. Usually one of: request, response :type header_type: str :param headers: Extra headers :type headers: dict :param body: The part that will become the body, or payload, of the message. :type body: str, dict, list :param callback: A pointer to the function to return results to. This function will receive 4 arguments: sendInfo (Dict) - Various details of the sent packet. deliver (Dict) - Deliver fields as returned by Pika. props (Pika Object) - Message properties, includes headers. msg (dict) - The actual content of the message. :type callback: function :param body: The body contents for the mesage. :type body: dict :return: A dictionary that can be directly returned to Yombo Gateways via AMQP :rtype: dict """ # print("body: %s" % body) request_msg = { "exchange_name": exchange_name, "routing_key": '*', "body": msgpack.dumps(body), "properties": { # "correlation_id" : correlation_id, "user_id": self. gateway_id, # system id is required to be able to send it. "content_type": 'application/msgpack', "headers": { # "requesting_user_id" : user "source": source + ":" + self.gateway_id, "destination": destination, "type": header_type, "protocol_verion": PROTOCOL_VERSION, "message_id": random_string(length=20), "msg_created_at": str(time()), }, }, "meta": { "content_type": 'application/msgpack', }, "created_at": time(), } if "callback" is not None: request_msg['callback'] = callback if correlation_id is None: request_msg['properties']['correlation_id'] = random_string( length=24) else: request_msg['properties']['correlation_id'] = correlation_id if message_id is None: request_msg['properties']['message_id'] = random_string(length=26) else: request_msg['properties']['message_id'] = message_id # Lets test if we can compress. Set headers as needed. if len(request_msg['body']) > 800: beforeZlib = len(request_msg['body']) request_msg['body'] = zlib.compress( request_msg['body'], 5 ) # 5 appears to be the best speed/compression ratio - MSchwenk afterZlib = len(request_msg['body']) request_msg['meta']['compression_percent'] = percentage( afterZlib, beforeZlib) request_msg['properties']['content_encoding'] = "zlib" else: request_msg['properties']['content_encoding'] = 'text' request_msg['properties']['headers'].update(headers) request_msg['meta']['content_encoding'] = request_msg['properties'][ 'content_encoding'] request_msg['meta']['payload_size'] = len(request_msg['body']) return request_msg
def amqp_incoming(self, deliver, properties, msg, queue): """ All incoming messages come here. It will be parsed and sorted as needed. Routing: 1) Device updates, changes, deletes -> Devices library 1) Command updates, changes, deletes -> Command library 1) Module updates, changes, deletes -> Module library 1) Device updates, changes, deletes -> Devices library 1) Device updates, changes, deletes -> Devices library Summary of tasks: 1) Validate incoming headers. 2) Setup ACK/Nack responses. 3) Route the message to the proper library for final handling. """ # self._local_log("info", "AMQPLibrary::amqp_incoming") # print " !!!!!!!!!!!!!!!!!!!!!!!!! " # print "properties: %s" % properties # log.msg('%s (%s): %s' % (deliver.exchange, deliver.routing_key, repr(msg)), system='Pika:<=') if properties.user_id is None: self._Statistics.increment("lib.amqpyombo.received.discarded.nouserid", bucket_time=15, anon=True) raise YomboWarning("user_id missing.") if properties.content_type is None: self._Statistics.increment("lib.amqpyombo.received.discarded.content_type_missing", bucket_time=15, anon=True) raise YomboWarning("content_type missing.") if properties.content_encoding is None: self._Statistics.increment("lib.amqpyombo.received.discarded.content_encoding_missing", bucket_time=15, anon=True) raise YomboWarning("content_encoding missing.") if properties.content_encoding != 'text' and properties.content_encoding != 'zlib': self._Statistics.increment("lib.amqpyombo.received.discarded.content_encoding_invalid", bucket_time=15, anon=True) raise YomboWarning("Content Encoding must be either 'text' or 'zlib'. Got: " + properties.content_encoding) if properties.content_type != 'text/plain' and properties.content_type != 'application/msgpack' and properties.content_type != 'application/json': self._Statistics.increment("lib.amqpyombo.received.discarded.content_type_invalid", bucket_time=15, anon=True) logger.warn('Error with contentType!') raise YomboWarning("Content type must be 'application/msgpack', 'application/json' or 'text/plain'. Got: " + properties.content_type) if properties.content_encoding == 'zlib': beforeZlib = len(msg) msg = zlib.decompress(msg) afterZlib = len(msg) logger.debug("Message sizes: msg_size_compressed = {compressed}, non-compressed = {uncompressed}, percent: {percent}", compressed=beforeZlib, uncompressed=afterZlib, percent=percentage(beforeZlib, afterZlib)) self._Statistics.increment("lib.amqpyombo.received.compressed", bucket_time=15, anon=True) self._Statistics.averages("lib.amqpyombo.received.compressed.percentage", percentage(beforeZlib, afterZlib), bucket_time=15, anon=True) else: self._Statistics.increment("lib.amqpyombo.received.uncompressed", bucket_time=15, anon=True) self._Statistics.averages("lib.amqpyombo.received.payload.size", len(msg), bucket_time=15, anon=True) if properties.content_type == 'application/json': if self.is_json(msg): msg = json.loads(msg) else: raise YomboWarning("Receive msg reported json, but isn't.") elif properties.content_type == 'application/msgpack': if self.is_msgpack(msg): msg = msgpack.loads(msg) else: raise YomboWarning("Received msg reported msgpack, but isn't.") if properties.headers['type'] == 'request': self._Statistics.increment("lib.amqpyombo.received.request", bucket_time=15, anon=True) # if a response, lets make sure it's something we asked for! elif properties.headers['type'] == "response": # print "send_correlation_ids: %s" % self.amqp.send_correlation_ids if properties.correlation_id not in self.amqp.send_correlation_ids: self._Statistics.increment("lib.amqpyombo.received.discarded.correlation_id_missing", bucket_time=15, anon=True) raise YomboWarning("correlation_id missing.") time_info = self.amqp.send_correlation_ids[properties.correlation_id] daate_time = time_info['time_received'] - time_info['time_sent'] milliseconds = ( daate_time.days * 24 * 60 * 60 + daate_time.seconds) * 1000 + daate_time.microseconds / 1000.0 logger.debug("Time between sending and receiving a response:: {milliseconds}", milliseconds=milliseconds) self._Statistics.averages("lib.amqpyombo.amqp.response.time", milliseconds, bucket_time=15, anon=True) if properties.correlation_id is None or not isinstance(properties.correlation_id, six.string_types): self._Statistics.increment("lib.amqpyombo.received.discarded.correlation_id_invalid", bucket_time=15, anon=True) raise YomboWarning("Correlation_id must be present for 'Response' types, and must be a string.") if properties.correlation_id not in self.amqp.send_correlation_ids: logger.debug("{correlation_id} not in list of ids: {send_correlation_ids} ", correlation_id=properties.correlation_id, send_correlation_ids=self.amqp.send_correlation_ids.keys()) self._Statistics.increment("lib.amqpyombo.received.discarded.nocorrelation", bucket_time=15, anon=True) raise YomboWarning("Received request {correlation_id}, but never asked for it. Discarding", correlation_id=properties.correlation_id) else: self._Statistics.increment("lib.amqpyombo.received.discarded.unknown_msg_type", bucket_time=15, anon=True) raise YomboWarning("Unknown message type recieved.") # self._local_log("debug", "PikaProtocol::receive_item4") # if we are here.. we have a valid message.... if properties.headers['type'] == 'request': try: logger.debug("headers: {headers}", headers=properties.headers) if properties.headers['request_type'] == 'control': self.controlHandler.process_control(msg, properties) elif properties.headers['request_type'] == 'system': self.process_system_request(msg, properties) except Exception, e: logger.error("--------==(Error: in response processing )==--------") logger.error("--------------------------------------------------------") logger.error("{error}", error=sys.exc_info()) logger.error("---------------==(Traceback)==--------------------------") logger.error("{trace}", trace=traceback.print_exc(file=sys.stdout)) logger.error("--------------------------------------------------------")
def generate_message(self, exchange_name, source, destination, header_type, headers, body, callback=None): """ When interacting with Yombo AMQP servers, we use a standard messaging layout. The below helps other functions and libraries conform to this standard. This only creates the message, it doesn't send it. Use the publish() function to complete that. **Usage**: .. code-block:: python requestData = { "exchange_name" : "gw_config", "source" : "yombo.gateway.lib.configurationupdate", "destination" : "yombo.server.configs", "callback" : self.amqp_direct_incoming, "body" : { "DataType" : "Object", "Request" : requestContent, }, } request = self.AMQPYombo.generateRequest(**requestData) :param exchange_name: The exchange the request should go to. :type exchange_name: str :param source: Value for the 'source' field. :type source: str :param destination: Value of the 'destination' field. :type destination: str :param callback: A pointer to the function to return results to. This function will receive 4 arguments: sendInfo (Dict) - Various details of the sent packet. deliver (Dict) - Deliver fields as returned by Pika. props (Pika Object) - Message properties, includes headers. msg (dict) - The actual content of the message. :type callback: function :param body: The body contents for the mesage. :type body: dict :return: A dictionary that can be directly returned to Yombo Gateways via AMQP :rtype: dict """ request_msg = { "exchange_name" : exchange_name, "routing_key" : '*', "body" : msgpack.dumps(body), "properties" : { # "correlation_id" : correlation_id, "user_id" : self.user_id, "content_type" : 'application/msgpack', "headers" : { "source" : source + ":" + self.user_id, "destination" : destination, "type" : header_type, "protocol_verion": PROTOCOL_VERSION, }, }, "callback": callback, } # Lets test if we can compress. Set headers as needed. self._Statistics.averages("lib.amqpyombo.sent.size", len(request_msg['body']), bucket_time=15, anon=True) if len(request_msg['body']) > 800: beforeZlib = len(request_msg['body']) request_msg['body'] = zlib.compress(request_msg['body'], 5) # 5 appears to be the best speed/compression ratio - MSchwenk request_msg['properties']['content_encoding'] = "zlib" afterZlib = len(request_msg['body']) self._Statistics.increment("lib.amqpyombo.sent.compressed", bucket_time=15, anon=True) self._Statistics.averages("lib.amqpyombo.sent.compressed.percentage", percentage(afterZlib, beforeZlib), anon=True) else: request_msg['properties']['content_encoding'] = 'text' self._Statistics.increment("lib.amqpyombo.sent.uncompressed", bucket_time=15, anon=True) request_msg['properties']['headers'].update(headers) return request_msg
def status_update(self, house, unit, command, status=None, deviceObj=None): """ Called by interface modules when a device has a change of status. """ logger.info("x10api - status update: {house}{unit}:{command}", house=house, unit=unit, command=command) unit = int(unit) if deviceObj is None: if house in self.x10_devices and unit in self.x10_devices[house]: deviceObj = self.x10_devices[house][unit] else: YomboWarning( "X10 API received a status update, but no device object to reference it." ) newstatus = None humanstatus = None tempcmd = command.upper() # self._DevicesByType('x10_appliance') device_type = self._DeviceTypes[deviceObj.device_type_id] logger.debug( "self._DevicesByType('x10_appliance'): {dt}", dt=self._DeviceTypes.devices_by_device_type('x10_appliance')) logger.debug("device_type: {dt}", dt=device_type.label) if device_type.machine_label == 'x10_appliance': logger.debug("in x10 appliance") if tempcmd == 'ON': newstatus = 1 humanstatus = 'On' elif tempcmd == 'OFF': newstatus = 0 humanstatus = 'Off' elif device_type.machine_label == 'x10_lamp': # basic lamp logger.debug("in x10 lamp") if tempcmd == 'ON': newstatus = 1 humanstatus = '100%' elif tempcmd == 'OFF': newstatus = 0 humanstatus = 'Off' elif tempcmd == 'DIM': if type(deviceObj.status[0]['status']) is int: newstatus = percentage(deviceObj.status[0]['status'] - 12 / 100) else: newstatus = 0.88 humanstatus = '88%' elif tempcmd == 'BRIGHT': if type(deviceObj.status[0]['status']) is int: newstatus = percentage(deviceObj.status[0]['status'] + 12 / 100) else: newstatus = 100 humanstatus = '100%' if type(newstatus) is int: if newstatus > 1: newstatus = 1 humanstatus = '100%' elif newstatus < 0: newstatus = 0 humanstatus = 'Off' else: newstatus = 0 humanstatus = 'Off' logger.debug( "status update. Machine: {newstatus} Human: {humanstatus}", newstatus=newstatus, humanstatus=humanstatus) human_message = "%s is now %s. " % (deviceObj.area_label, humanstatus.lower()) deviceObj.set_status(machine_status=newstatus, human_status=humanstatus, human_message=human_message, source="x10api")