def setUp(self): HttpTestBase.setUp(self) self.client_manager = TCPClientManager(self.dc.get_connection())
def __init__(self, conn): APIBase.__init__(self, conn) # TODO: determine best way to expose additional options self._tcp_client_manager = TCPClientManager(self._conn, secure=True)
class MonitorAPI(APIBase): """Provide access to Device Cloud Monitor API for receiving push notifications The Monitor API in Device Cloud allows for the creation and destruction of multiple "monitors." Each monitor is registered against one or more "topics" which describe the data in which it is interested. There are, in turn, two main ways to receive data matching the topics for a given monitor: 1. Stream: Device Cloud supports a protocol over TCP (optionally with SSL) over which the batches of events will be sent when they are received. 2. HTTP: When batches of events are received, a configured web service endpoint will received a POST request with the new data. Currently, this library supports setting up both types of monitors, but there is no special support provided for parsing HTTP postback requests. More information on the format for topic strings can be found in the `device cloud documentation for monitors <http://goo.gl/6UiOCG>`_. Here's a quick example showing a typical pattern used for creating a push monitor and associated listener that triggers a callback. Deletion of existing monitors matching the same topics is not necessary but sometimes done in order to ensure that changes to the monitor configuration in code always make it to the monitor configuration in Device Cloud:: def monitor_callback(json_data): print(json_data) return True # message received # Listen for DataPoint updates topics = ['DataPoint[U]'] monitor = dc.monitor.get_monitor(topics) if monitor: monitor.delete() monitor = dc.monitor.create_tcp_monitor(topics) monitor.add_listener(monitor_callback) # later... dc.monitor.stop_listeners() When updates to any DataPoint in Device Cloud occurs, the callback will be called with a data structure like this one:: {'Document': {'Msg': {'DataPoint': {'cstId': 7603, 'data': 0.411700824929, 'description': '', 'id': '684572e0-12c4-11e5-8507-fa163ed4cf14', 'quality': 0, 'serverTimestamp': 1434307047694, 'streamId': 'test', 'streamUnits': '', 'timestamp': 1434307047694}, 'group': '*', 'operation': 'INSERTION', 'timestamp': '2015-06-14T18:37:27.815Z', 'topic': '7603/DataPoint/test'}}} """ def __init__(self, conn): APIBase.__init__(self, conn) # TODO: determine best way to expose additional options self._tcp_client_manager = TCPClientManager(self._conn, secure=True) def create_tcp_monitor(self, topics, batch_size=1, batch_duration=0, compression='gzip', format_type='json'): """Creates a TCP Monitor instance in Device Cloud for a given list of topics :param topics: a string list of topics (e.g. ['DeviceCore[U]', 'FileDataCore']). :param batch_size: How many Msgs received before sending data. :param batch_duration: How long to wait before sending batch if it does not exceed batch_size. :param compression: Compression value (i.e. 'gzip'). :param format_type: What format server should send data in (i.e. 'xml' or 'json'). Returns an object of the created Monitor """ monitor_xml = """\ <Monitor> <monTopic>{topics}</monTopic> <monBatchSize>{batch_size}</monBatchSize> <monFormatType>{format_type}</monFormatType> <monTransportType>tcp</monTransportType> <monCompression>{compression}</monCompression> </Monitor> """.format( topics=','.join(topics), batch_size=batch_size, batch_duration=batch_duration, format_type=format_type, compression=compression, ) monitor_xml = textwrap.dedent(monitor_xml) response = self._conn.post("/ws/Monitor", monitor_xml) location = ET.fromstring(response.text).find('.//location').text monitor_id = int(location.split('/')[-1]) return TCPDeviceCloudMonitor(self._conn, monitor_id, self._tcp_client_manager) def create_http_monitor(self, topics, transport_url, transport_token=None, transport_method='PUT', connect_timeout=0, response_timeout=0, batch_size=1, batch_duration=0, compression='none', format_type='json'): """Creates a HTTP Monitor instance in Device Cloud for a given list of topics :param topics: a string list of topics (e.g. ['DeviceCore[U]', 'FileDataCore']). :param transport_url: URL of the customer web server. :param transport_token: Credentials for basic authentication in the following format: username:password :param transport_method: HTTP method to use for sending data: PUT or POST. The default is PUT. :param connect_timeout: A value of 0 means use the system default of 5000 (5 seconds). :param response_timeout: A value of 0 means use the system default of 5000 (5 seconds). :param batch_size: How many Msgs received before sending data. :param batch_duration: How long to wait before sending batch if it does not exceed batch_size. :param compression: Compression value (i.e. 'gzip'). :param format_type: What format server should send data in (i.e. 'xml' or 'json'). Returns an object of the created Monitor """ monitor_xml = """\ <Monitor> <monTopic>{topics}</monTopic> <monBatchSize>{batch_size}</monBatchSize> <monFormatType>{format_type}</monFormatType> <monTransportType>http</monTransportType> <monTransportUrl>{transport_url}</monTransportUrl> <monTransportToken>{transport_token}</monTransportToken> <monTransportMethod>{transport_method}</monTransportMethod> <monConnectTimeout>{connect_timeout}</monConnectTimeout> <monResponseTimeout>{response_timeout}</monResponseTimeout> <monCompression>{compression}</monCompression> </Monitor> """.format( topics=','.join(topics), transport_url=transport_url, transport_token=transport_token, transport_method=transport_method, connect_timeout=connect_timeout, response_timeout=response_timeout, batch_size=batch_size, batch_duration=batch_duration, format_type=format_type, compression=compression, ) monitor_xml = textwrap.dedent(monitor_xml) response = self._conn.post("/ws/Monitor", monitor_xml) location = ET.fromstring(response.text).find('.//location').text monitor_id = int(location.split('/')[-1]) return HTTPDeviceCloudMonitor(self._conn, monitor_id) def get_monitors(self, condition=None, page_size=1000): """Return an iterator over all monitors matching the provided condition Get all inactive monitors and print id:: for mon in dc.monitor.get_monitors(MON_STATUS_ATTR == "DISABLED"): print(mon.get_id()) Get all the HTTP monitors and print id:: for mon in dc.monitor.get_monitors(MON_TRANSPORT_TYPE_ATTR == "http"): print(mon.get_id()) Many other possibilities exist. See the :mod:`devicecloud.condition` documention for additional details on building compound expressions. :param condition: An :class:`.Expression` which defines the condition which must be matched on the monitor that will be retrieved from Device Cloud. If a condition is unspecified, an iterator over all monitors for this account will be returned. :type condition: :class:`.Expression` or None :param int page_size: The number of results to fetch in a single page. :return: Generator yielding :class:`.DeviceCloudMonitor` instances matching the provided conditions. """ req_kwargs = {} if condition: req_kwargs['condition'] = condition.compile() for monitor_data in self._conn.iter_json_pages("/ws/Monitor", **req_kwargs): yield DeviceCloudMonitor.from_json(self._conn, monitor_data, self._tcp_client_manager) def get_monitor(self, topics): """Attempts to find a Monitor in device cloud that matches the provided topics :param topics: a string list of topics (e.g. ``['DeviceCore[U]', 'FileDataCore'])``) Returns a :class:`DeviceCloudMonitor` if found, otherwise None. """ for monitor in self.get_monitors(MON_TOPIC_ATTR == ",".join(topics)): return monitor # return the first one, even if there are multiple return None def stop_listeners(self): """Stop any listener threads that may be running and join on them""" self._tcp_client_manager.stop()
def __init__(self, conn): MonitorAPI.__init__(self, conn) self._tcp_client_manager = TCPClientManager(self._conn, secure=False)