def setUp(self): """Mock the HTTP request method so that the long poll does not received anything.""" self.patch = mock.patch('urllib3.PoolManager.request') mocked = self.patch.start() mocked.return_value.data = b'' mocked.return_value.status = 200 self.api = ConnectAPI(dict(autostart_notification_thread=False))
def test_observer_timeout(self): """Test that the observer timeouts""" from mbed_cloud.connect import ConnectAPI api = ConnectAPI() observer = api.subscribe( api.subscribe.channels.DeviceStateChanges(device_id=123456)) with self.assertRaises(CloudTimeoutError) as timeout_error: observer.next().block(timeout=2) self.assertEqual(str(timeout_error.exception), "No data received after 2.0 seconds.")
def worker(start, api_key, results): print('hi') start.wait() # let's all start at the same time print('go') api = ConnectAPI(dict(api_key=api_key)) time.sleep(random.triangular(0.1, 0.2, 0.5)) codegen_layer = api._get_api(NotificationsApi) key_set = codegen_layer.api_client.configuration.api_key['Authorization'] print('get/set %r %r' % (api_key, key_set)) results.append(key_set)
def test_live_device_state_change(self): # integration - DeviceStateChanges local filter from mbed_cloud.connect import ConnectAPI api = ConnectAPI() d = api.list_connected_devices().first() observer = api.subscribe(api.subscribe.channels.DeviceStateChanges(device_id=d.id)) # cheat, waiting takes too long api.subscribe.notify({ channels.ChannelIdentifiers.reg_updates: [ dict(a=1, b=2, device_id=d.id), dict(a=1, b=2, device_id='A'), dict(a=1, b=2, device_id='B'), ] }) r = observer.next().block(timeout=2) self.assertTrue(r)
def test_live_device_state_change(self): api = ConnectAPI() api.delete_presubscriptions() d = api.list_connected_devices().first() api.delete_device_subscriptions(d.id) channel = channels.ResourceValues(device_id=d.id, resource_path=['/3/0/*']) observer = api.subscribe(channel) # don't actually care about notifications, we want to see subscriptions current_subs = api.list_device_subscriptions(d.id) self.assertGreaterEqual(len(current_subs), 3) for path in current_subs: self.assertIn('/3/0', path) channel.stop() current_subs = api.list_device_subscriptions(d.id) self.assertIsNone(None, current_subs)
def test_live_device_state_change(self): api = ConnectAPI() d = api.list_connected_devices().first() api.delete_device_subscriptions(d.id) # request the `manufacturer` field channel = channels.CurrentResourceValue(device_id=d.id, resource_path='/3/0/0') observer = api.subscribe(channel) value = observer.next().block(timeout=2) # should be, in this case, TLV self.assertEqual(b'6465765f6d616e756661637475726572', value)
def test_live_create_and_cleanup(self): # integration - ResourceValueCurrent cleans up from mbed_cloud.connect import ConnectAPI api = ConnectAPI() d = api.list_connected_devices().first() r = api.subscribe(api.subscribe.channels.ResourceValues( device_id=d.id, resource_path='/3/0/0', )).next().block(timeout=2) self.assertTrue(r) # wait for notifications to be stopped api.stop_notifications() # check that the routing table is cleaned up for i in range(10): time.sleep(0.01) if not api.subscribe.list_all(): break else: self.fail('Routing table not empty')
class SDKTests(BaseHostTest): __result = None deviceApi = None connectApi = None deviceID = None iteration = None post_timeout = None def send_safe(self, key, value): #self.send_kv('dummy_start', 0) self.send_kv(key, value) self.send_kv(key, value) self.send_kv(key, value) self.send_kv(key, value) self.send_kv(key, value) #self.send_kv('dummy_end', 1) def test_steps(self): # Step 0 set up test system_reset = yield # Step 1 device connected to Pelion should reset. self.send_safe('reset', 0) time.sleep(self.program_cycle_s) self.send_safe('__sync', 0) self.iteration = self.iteration + 1 system_reset = yield #Step 2, finish yield True def _callback_device_ready(self, key, value, timestamp): # Send device iteration number after a reset self.send_safe('iteration', self.iteration) def _callback_advance_test(self, key, value, timestamp): # Advance test sequence try: if self.test_steps_sequence.send(None): self.notify_complete(True) except (StopIteration, RuntimeError) as exc: self.notify_complete(False) def _callback_device_api_registration(self, key, value, timestamp): try: #set value for later use self.deviceID = value # Check if device is in Mbed Cloud Device Directory device = self.deviceApi.get_device(value) # Send registraton status to device self.send_safe("registration", 1 if device.state == "registered" else 0) except: # SDK throws an exception if the device is not found (unsuccessful registration) or times out self.send_safe("registration", 0) def _callback_device_verification(self, key, value, timestamp): # Send true if old DeviceID is the same as current device is self.send_safe("verification", 1 if self.deviceID == value else 0) def _callback_fail_test(self, key, value, timestamp): # Test failed. End it. self.notify_complete(False) def _callback_device_lwm2m_get_verification(self, key, value, timestamp): timeout = 0 # Get resource value from device async_response = self.connectApi.get_resource_value_async( self.deviceID, value) # Set a 30 second timeout here. while not async_response.is_done and timeout <= 300: time.sleep(0.1) timeout += 1 if async_response.is_done: # Send resource value back to device self.send_safe("get_value", async_response.value) else: # Request timed out. self.send_safe("timeout", 0) def _callback_device_lwm2m_set_verification(self, key, value, timestamp): timeout = 0 # Get resource value from device async_response = self.connectApi.get_resource_value_async( self.deviceID, value) # Set a 30 second timeout here. while not async_response.is_done and timeout <= 300: time.sleep(0.1) timeout += 1 if async_response.is_done: # Send resource value back to device self.send_safe("set_value", async_response.value) else: # Request timed out. self.send_safe("timeout", 0) def _callback_device_lwm2m_put_verification(self, key, value, timestamp): timeout = 0 # Get resource value from device and increment it resource_value = self.connectApi.get_resource_value_async( self.deviceID, value) # Set a 30 second timeout here. while not resource_value.is_done and timeout <= 300: time.sleep(0.1) timeout += 1 if not resource_value.is_done: self.send_safe("timeout", 0) return updated_value = int(resource_value.value) + 5 # Set new resource value from cloud async_response = self.connectApi.set_resource_value_async( self.deviceID, value, updated_value) # Set a 30 second timeout here. while not async_response.is_done and timeout <= 300: time.sleep(0.1) timeout += 1 if not async_response.is_done: self.send_safe("timeout", 0) else: # Send new resource value to device for verification. self.send_safe("res_set", updated_value) def _callback_device_lwm2m_post_verification(self, key, value, timestamp): timeout = 0 # Execute POST function on device resource_value = self.connectApi.execute_resource_async( self.deviceID, value) # Set a 30 second timeout here. while not resource_value.is_done and timeout <= 300: time.sleep(0.1) timeout += 1 if not resource_value.is_done: self.send_safe("timeout", 0) self.post_timeout = 1 def _callback_device_lwm2m_post_verification_result( self, key, value, timestamp): # Called from callback function on device, POST function working as expected. # If post_timeout is not none, the request took longer than 30 seconds, which is # a failure. Don't send this value. if not self.post_timeout: self.send_safe("post_test_executed", 0) def setup(self): #Start at iteration 0 self.iteration = 0 # Register callbacks from GT tests self.register_callback('device_api_registration', self._callback_device_api_registration) self.register_callback('advance_test', self._callback_advance_test) self.register_callback('device_ready', self._callback_device_ready) self.register_callback('device_verification', self._callback_device_verification) self.register_callback('fail_test', self._callback_fail_test) self.register_callback('device_lwm2m_get_test', self._callback_device_lwm2m_get_verification) self.register_callback('device_lwm2m_set_test', self._callback_device_lwm2m_set_verification) self.register_callback('device_lwm2m_put_test', self._callback_device_lwm2m_put_verification) self.register_callback('device_lwm2m_post_test', self._callback_device_lwm2m_post_verification) self.register_callback( 'device_lwm2m_post_test_result', self._callback_device_lwm2m_post_verification_result) # Setup API config try: result = subprocess.check_output(["mbed", "config", "-G", "CLOUD_SDK_API_KEY"], \ stderr=subprocess.STDOUT) except Exception, e: print "Error: CLOUD_SDK_API_KEY global config is not set: " + str( e) return -1 result = str(result).split(' ') if result[1] == "No": print "Error: CLOUD_SDK_API_KEY global config is not set." return -1 # Get API KEY and remove LF char if included api_key_val = str(result[1]).rstrip() print "CLOUD_SDK_API_KEY: " + api_key_val api_config = { "api_key": api_key_val, "host": "https://api.us-east-1.mbedcloud.com" } # Instantiate Device and Connect API self.deviceApi = DeviceDirectoryAPI(api_config) self.connectApi = ConnectAPI(api_config)
def test_empty_string(self): self.assertEqual(ConnectAPI._base64_encode(""), "")
def test_string(self): self.assertEqual(ConnectAPI._base64_encode("Hello World"), "SGVsbG8gV29ybGQ=")
import os import pprint from mbed_cloud.connect import ConnectAPI TOKEN = "YOUR_ACCESS_KEY" # set up the Python SDK config = {} config['api_key'] = os.getenv('TOKEN') or TOKEN config['host'] = 'https://api.us-east-1.mbedcloud.com' api = ConnectAPI(config) api.start_notifications() devices = list( api.list_connected_devices(filters={'device_type': 'light-system'})) print("Found %d lights" % (len(devices)), [c.id for c in devices]) for device in devices: def pir_callback(device_id, path, count): print("Motion detected at %s, new count is %s" % (device_id, count)) api.add_resource_subscription_async(device.id, '/3201/0/5700', pir_callback) print("subscribed to resource") pink = 0xff69b4 api.set_resource_value(device.id, '/3311/0/5706', pink) print("set color to pink")
def test_positive_int(self): self.assertEqual(ConnectAPI._base64_encode(1), "MQ==")
# Use 'threading' async_mode, as we don't use greenlet threads for background threads # in SDK - and thus we can't use eventlet or gevent. async_mode = 'threading' # API key from https://portal.mbedcloud.com/. Can also be set by setting the value # with ACCESS_KEY environment variable. api_key = "" app = Flask(__name__) socket = SocketIO(app, async_mode=async_mode, logger=True, engineio_logger=True) #api = DeviceAPI({"api_key": os.environ.get("ACCESS_KEY", api_key)}) api = ConnectAPI({ "api_key": os.environ.get("ACCESS_KEY", api_key), "host": "https://lab-api.mbedcloudintegration.net" }) logging.basicConfig(stream=sys.stdout, level=logging.INFO) @app.route('/') def index(): devices = [] for device in api.list_connected_devices(): logging.info("Device Found: {}".format(device.id)) value = api.get_resource_value(device.id, BLINK_PATTERN_RESOURCE_PATH) devices.append({ 'id': device.id, 'blink_pattern': value.decode('utf-8') })
app.config.from_object(settings.Config) # Override params with CLI inputs if args.api_key_val: app.config["API_KEY"] = args.api_key_val if args.host_val: app.config["API_HOST"] = args.host_val WEBHOOK_URL = "%s/api/webhook" % app.config["API_BASE_URL"] PRODUCT_ID_PATH = "/10341/0/26341" PRODUCT_CURR_COUNT_PATH = "/10341/0/26342" # Instatiate cloud connect api_config = {"api_key": app.config["API_KEY"], "host": app.config["API_HOST"]} connectApi = ConnectAPI(api_config) # This should be required, why is it breaking things connectApi.start_notifications() logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) # Instantiate the database client on Table named 'example' # Set the username and password to root db = InfluxDBClient("influxdb", app.config["INFLUX_PORT"], 'root', 'root', 'example') id_num_db = {} def handleSubscribe(device_id, path, current_value): """On change in subscribed resource, dump data to InfluxDB."""
class TestGetResourceValue(BaseCase): def setUp(self): """Mock the HTTP request method so that the long poll does not received anything.""" self.patch = mock.patch('urllib3.PoolManager.request') mocked = self.patch.start() mocked.return_value.data = b'' mocked.return_value.status = 200 self.api = ConnectAPI(dict(autostart_notification_thread=False)) def tearDown(self): self.patch.stop() def test_async_wait(self): """Test a all registration notifications in a single message""" async_result = self.api.get_resource_value_async("abc123", "/3/0/0") example_data = { "async-responses": [{ "ct": "text/plain", "payload": "My4zMQ==", "max-age": "60", "id": async_result.async_id, "error": None, "status": 202 }], } self.api.notify_webhook_received(payload=json.dumps(example_data)) self.assertEqual('3.31', async_result.wait()) def test_async_wait_error(self): """Test a all registration notifications in a single message""" async_result = self.api.get_resource_value_async("abc123", "/3/0/0") example_data = { "async-responses": [{ "ct": "text/plain", "payload": "My4zMQ==", "max-age": "60", "id": async_result.async_id, "error": "TIMEOUT", "status": 504 }], } self.api.notify_webhook_received(payload=json.dumps(example_data)) # An Async response with an error should raise an exception with self.assertRaises(CloudAsyncError) as e: async_result.wait() self.assertTrue( str(e.exception).startswith("(504) 'TIMEOUT' Async response for")) self.assertEqual("TIMEOUT", e.exception.reason) self.assertEqual(504, e.exception.status) def test_async_value_error(self): """Test a all registration notifications in a single message""" async_result = self.api.get_resource_value_async("abc123", "/3/0/0") example_data = { "async-responses": [{ "ct": "text/plain", "payload": None, "max-age": "60", "id": async_result.async_id, "error": "AN ERROR", "status": 499 }], } self.api.notify_webhook_received(payload=json.dumps(example_data)) # Attempted to get the value when there is an error and no payload should raise an exception with self.assertRaises(CloudUnhandledError) as e: async_result.value self.assertEqual( "(499) 'AN ERROR' Attempted to decode async request which returned an error.", str(e.exception)) self.assertEqual("AN ERROR", e.exception.reason) self.assertEqual(499, e.exception.status)
- [optional] Visit `https://YOUR_NGROK_ID_GOES_HERE.ngrok.io/404` (a 404 page will indicate that your connection works) - Visit `http://127.0.0.1:8000/start` in your browser to initiate the sequence - View the result of the application in the terminal """ from mbed_cloud.connect import ConnectAPI import hug import os import sys import threading import traceback api = ConnectAPI() ngrok_url = sys.argv[-1] if len(sys.argv) == 4 else ( os.environ.get('NGROK_URL') or 'https://YOUR_NGROK_ID_GOES_HERE.ngrok.io') os.environ['NGROK_URL'] = ngrok_url resource_path = "/3/0/2" def my_application(api): """An example application. - Registers a webhook with mbed cloud services - Requests the value of a resource - Prints the value when it arrives """ device = api.list_connected_devices().first() print('using device #', device.id)
def test_negative_int(self): self.assertEqual(ConnectAPI._base64_encode(-1), "LTE=")
def test_positive_float(self): self.assertEqual(ConnectAPI._base64_encode(1.0), "MS4w")
def test_negative_float(self): self.assertEqual(ConnectAPI._base64_encode(-1.0), "LTEuMA==")
""" # an example: using a webhook for handling notifications from mbed cloud from mbed_cloud.connect import ConnectAPI import hug import os import sys import threading import traceback # we must disable automatic creation of a long-poll thread # as webhooks and long polling are mutually exclusive on mbed cloud api = ConnectAPI(dict(autostart_notification_thread=False)) ngrok_url = sys.argv[-1] if len(sys.argv) == 4 else ( os.environ.get('NGROK_URL') or 'https://YOUR_NGROK_ID_GOES_HERE.ngrok.io') os.environ['NGROK_URL'] = ngrok_url resource_path = "/3/0/2" def my_application(api): """An example application. - Registers a webhook with mbed cloud services - Requests the value of a resource - Prints the value when it arrives """ device = api.list_connected_devices().first() print('using device #', device.id)
def test_none(self): self.assertEqual(ConnectAPI._base64_encode(None), "")
import signal import threading import traceback import binascii import json from collections import namedtuple from mbed_cloud.connect import ConnectAPI from mbed_cloud.device_directory import DeviceDirectoryAPI from mbed_cloud.exceptions import CloudApiException wait_condition = threading.Condition() wait_condition.acquire() keep_running = True API_connect = ConnectAPI() API_device_directory = DeviceDirectoryAPI() def byte_to_hex(value): return binascii.hexlify(value) def byte_to_int(value): if len(value) == 2: # unsigned short, uint16_t return struct.unpack("<H", value)[0] elif len(value) == 4: # unsigned int, uint32_t return struct.unpack("<i", value)[0] else:
def test_registration_notifications(self): """Test a all registration notifications in a single message""" from mbed_cloud.connect import ConnectAPI device_id = "015f3850a657000000000001001002ab" api = ConnectAPI() registrations_observer = api.subscribe( api.subscribe.channels.DeviceStateChanges( device_id=device_id, channel=channels.ChannelIdentifiers.registrations), provider=False) de_registrations_observer = api.subscribe( api.subscribe.channels.DeviceStateChanges( device_id=device_id, channel=channels.ChannelIdentifiers.de_registrations)) reg_updates_observer = api.subscribe( api.subscribe.channels.DeviceStateChanges( device_id=device_id, channel=channels.ChannelIdentifiers.reg_updates)) registrations_expired_observer = api.subscribe( api.subscribe.channels.DeviceStateChanges( device_id=device_id, channel=channels.ChannelIdentifiers.registrations_expired)) # cheat, waiting takes too long example_data = { "registrations": [{ "q": False, "original-ep": "my-device-123", "ept": "Light", "resources": [{ "path": "/sen/light", "ct": "text/plain", "obs": True, "rt": "light_sensor", "if": "sensor" }], "ep": device_id }], "reg_updates": [{ "q": False, "original-ep": "my-device-123", "ept": "Light", "resources": [{ "path": "/sen/light", "ct": "text/plain", "obs": True, "rt": "light_sensor", "if": "sensor" }], "ep": device_id }], "async_responses": [{ "ct": "text/plain", "payload": "My4zMQ==", "max-age": "60", "id": "9e3c96b8-c4d7-496a-ab90-cc732b9b560e", "error": "TIMEOUT", "status": 200 }], "notifications": [{ "path": "/sen/light", "ct": "text/plain", "payload": "My4zMQ==", "max-age": "60", "ep": device_id }], "de_registrations": [device_id], "registrations_expired": [device_id], } api.subscribe.notify(example_data) self.assertEqual( registrations_observer.next().block(timeout=2), { 'alias': None, 'channel': 'registrations', 'device_id': '015f3850a657000000000001001002ab', 'device_type': 'Light', 'queue_mode': False, 'resources': [{ 'content_type': 'text/plain', 'observable': True, 'path': '/sen/light', 'type': 'light_sensor' }] }) self.assertEqual( de_registrations_observer.next().block(timeout=2), { 'channel': 'de_registrations', 'device_id': '015f3850a657000000000001001002ab' }) self.assertEqual( reg_updates_observer.next().block(timeout=2), { 'alias': None, 'channel': 'reg_updates', 'device_id': '015f3850a657000000000001001002ab', 'device_type': 'Light', 'queue_mode': False, 'resources': [{ 'content_type': 'text/plain', 'observable': True, 'path': '/sen/light', 'type': 'light_sensor' }] }) self.assertEqual( registrations_expired_observer.next().block(timeout=2), { 'channel': 'registrations_expired', 'device_id': '015f3850a657000000000001001002ab' })