async def main(): """Main entrypoint.""" LOGGER.info("Creating WebSocket server on: {}".format(PORT_WS)) ws_server = WebsocketServer(port=PORT_WS) LOGGER.info("Creating HTTP server on: {}".format(PORT_HTTP)) http_server = HTTPServer(port=PORT_HTTP) LOGGER.info("Creating MQTT server on broker: {}".format(MQTT_BROKER)) mqtt_server = MQTTServer(MQTT_BROKER) LOGGER.info( "Creating servient with TD catalogue on: {}".format(PORT_CATALOGUE)) servient = Servient(catalogue_port=PORT_CATALOGUE) servient.add_server(ws_server) servient.add_server(http_server) servient.add_server(mqtt_server) LOGGER.info("Starting servient") wot = await servient.start() LOGGER.info("Exposing System Monitor Thing") exposed_thing = wot.produce(json.dumps(DESCRIPTION)) exposed_thing.set_property_read_handler("cpuPercent", cpu_percent_handler) exposed_thing.properties["cpuThreshold"].write(DEFAULT_CPU_THRESHOLD) exposed_thing.expose() create_cpu_check_task(exposed_thing)
def main(): LOGGER.info("Creating COAP server on: {}".format(COAP_PORT)) coap_server = CoAPServer(port=COAP_PORT) LOGGER.info("Creating servient with TD catalogue on: {}".format(CATALOGUE_PORT)) servient = Servient(catalogue_port=CATALOGUE_PORT) servient.add_server(coap_server) LOGGER.info("Starting servient") wot = yield servient.start() LOGGER.info("Exposing and configuring Thing") exposed_thing = wot.produce(json.dumps(DESCRIPTION)) exposed_thing.expose()
def main(): ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_context.load_cert_chain(certfile="../rsa.crt", keyfile="../rsa.key") LOGGER.info("Creating HTTPs server on: {}".format(HTTP_PORT)) http_server = HTTPServer(port=HTTP_PORT, ssl_context=ssl_context) LOGGER.info( "Creating servient with TD catalogue on: {}".format(CATALOGUE_PORT)) servient = Servient(catalogue_port=CATALOGUE_PORT) servient.add_server(http_server) LOGGER.info("Starting servient") wot = yield servient.start() LOGGER.info("Exposing and configuring Thing") exposed_thing = wot.produce(json.dumps(DESCRIPTION)) exposed_thing.expose()
def all_protocols_servient(): """Returns a Servient configured to use all available protocol bindings.""" servient = Servient(catalogue_port=None) http_port = find_free_port() http_server = HTTPServer(port=http_port) servient.add_server(http_server) ws_port = find_free_port() ws_server = WebsocketServer(port=ws_port) servient.add_server(ws_server) if is_coap_supported(): from wotpy.protocols.coap.server import CoAPServer coap_port = find_free_port() coap_server = CoAPServer(port=coap_port) servient.add_server(coap_server) if is_mqtt_supported(): from wotpy.protocols.mqtt.server import MQTTServer from tests.protocols.mqtt.broker import get_test_broker_url, is_test_broker_online if is_test_broker_online(): mqtt_server = MQTTServer(broker_url=get_test_broker_url()) servient.add_server(mqtt_server) @tornado.gen.coroutine def start(): raise tornado.gen.Return((yield servient.start())) wot = tornado.ioloop.IOLoop.current().run_sync(start) td_dict = { "id": uuid.uuid4().urn, "title": uuid.uuid4().hex, "properties": { uuid.uuid4().hex: { "observable": True, "type": "string" } } } td = ThingDescription(td_dict) exposed_thing = wot.produce(td.to_str()) exposed_thing.expose() yield servient @tornado.gen.coroutine def shutdown(): yield servient.shutdown() tornado.ioloop.IOLoop.current().run_sync(shutdown)
def main(): global buf LOGGER.info("Creating HTTP server on: {}".format(HTTP_PORT)) http_server = HTTPServer(port=HTTP_PORT) LOGGER.info("Creating servient with TD catalogue on: {}".format(CATALOGUE_PORT)) servient = Servient(catalogue_port=CATALOGUE_PORT) servient.add_server(http_server) while len(buf) < int(sys.argv[1]): r = random.random() c = math.floor(r*256) buf += format(c, 'x') LOGGER.info("Starting servient") wot = yield servient.start() LOGGER.info("Exposing and configuring Thing") exposed_thing = wot.produce(json.dumps(DESCRIPTION)) exposed_thing.set_property_read_handler("bench", bench_handler) exposed_thing.expose()
def main(): global buf ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_context.load_cert_chain(certfile="../rsa.crt", keyfile="../rsa.key") LOGGER.info("Creating HTTPs server on: {}".format(HTTP_PORT)) http_server = HTTPServer(port=HTTP_PORT, ssl_context=ssl_context) LOGGER.info("Creating servient with TD catalogue on: {}".format(CATALOGUE_PORT)) servient = Servient(catalogue_port=CATALOGUE_PORT) servient.add_server(http_server) while len(buf) < int(sys.argv[1]): r = random.random() c = math.floor(r*256) buf += format(c, 'x') LOGGER.info("Starting servient") wot = yield servient.start() LOGGER.info("Exposing and configuring Thing") exposed_thing = wot.produce(json.dumps(DESCRIPTION)) exposed_thing.set_property_read_handler("bench", bench_handler) exposed_thing.expose()
def build_servient(parsed_args, clients_config=None): """Factory function to build a Servient with a set of servers depending on the input arguments.""" logger = logging.getLogger() logger.info("Creating servient with TD catalogue on: {}".format( parsed_args.port_catalogue)) servient = Servient(catalogue_port=parsed_args.port_catalogue, hostname=parsed_args.hostname, clients_config=clients_config) if parsed_args.port_ws > 0: logger.info("Creating WebSocket server on: {}".format( parsed_args.port_ws)) servient.add_server(WebsocketServer(port=parsed_args.port_ws)) if parsed_args.port_http > 0: logger.info("Creating HTTP server on: {}".format( parsed_args.port_http)) servient.add_server(HTTPServer(port=parsed_args.port_http)) if parsed_args.mqtt_broker: try: from wotpy.protocols.mqtt.server import MQTTServer logger.info("Creating MQTT server on broker: {}".format( parsed_args.mqtt_broker)) mqtt_server = MQTTServer(parsed_args.mqtt_broker, servient_id=servient.hostname) servient.add_server(mqtt_server) logger.info("MQTT server created with ID: {}".format( mqtt_server.servient_id)) except NotImplementedError as ex: logger.warning(ex) if parsed_args.port_coap > 0: try: from wotpy.protocols.coap.server import CoAPServer logger.info("Creating CoAP server on: {}".format( parsed_args.port_coap)) servient.add_server(CoAPServer(port=parsed_args.port_coap)) except NotImplementedError as ex: logger.warning(ex) return servient
def test_servient_start_stop(): """The servient and contained ExposedThings can be started and stopped.""" fake = Faker() ws_port = find_free_port() ws_server = WebsocketServer(port=ws_port) servient = Servient() servient.disable_td_catalogue() servient.add_server(ws_server) @tornado.gen.coroutine def start(): raise tornado.gen.Return((yield servient.start())) wot = tornado.ioloop.IOLoop.current().run_sync(start) thing_id = uuid.uuid4().urn name_prop_string = fake.user_name() name_prop_boolean = fake.user_name() td_doc = { "id": thing_id, "title": Faker().sentence(), "properties": { name_prop_string: { "observable": True, "type": "string" }, name_prop_boolean: { "observable": True, "type": "boolean" } } } td_str = json.dumps(td_doc) exposed_thing = wot.produce(td_str) exposed_thing.expose() value_boolean = fake.pybool() value_string = fake.pystr() @tornado.gen.coroutine def get_property(prop_name): """Gets the given property using the WS Link contained in the thing description.""" td = ThingDescription.from_thing(exposed_thing.thing) consumed_thing = ConsumedThing(servient, td=td) value = yield consumed_thing.read_property(prop_name) raise tornado.gen.Return(value) @tornado.gen.coroutine def assert_thing_active(): """Asserts that the retrieved property values are as expected.""" retrieved_boolean = yield get_property(name_prop_boolean) retrieved_string = yield get_property(name_prop_string) assert retrieved_boolean == value_boolean assert retrieved_string == value_string @tornado.gen.coroutine def test_coroutine(): yield exposed_thing.write_property(name=name_prop_boolean, value=value_boolean) yield exposed_thing.write_property(name=name_prop_string, value=value_string) yield assert_thing_active() exposed_thing.destroy() with pytest.raises(Exception): yield assert_thing_active() with pytest.raises(Exception): exposed_thing.expose() yield servient.shutdown() run_test_coroutine(test_coroutine)
def websocket_servient(): """Returns a Servient that exposes a Websockets server and one ExposedThing.""" ws_port = find_free_port() ws_server = WebsocketServer(port=ws_port) servient = Servient(catalogue_port=None) servient.add_server(ws_server) @tornado.gen.coroutine def start(): raise tornado.gen.Return((yield servient.start())) wot = tornado.ioloop.IOLoop.current().run_sync(start) property_name_01 = uuid.uuid4().hex property_name_02 = uuid.uuid4().hex action_name_01 = uuid.uuid4().hex event_name_01 = uuid.uuid4().hex td_dict = { "id": uuid.uuid4().urn, "name": uuid.uuid4().hex, "properties": { property_name_01: { "observable": True, "type": "string" }, property_name_02: { "observable": True, "type": "string" } }, "actions": { action_name_01: { "input": { "type": "object" }, "output": { "type": "string" }, } }, "events": { event_name_01: { "type": "string" } }, } td = ThingDescription(td_dict) exposed_thing = wot.produce(td.to_str()) exposed_thing.expose() @tornado.gen.coroutine def action_handler(parameters): input_value = parameters.get("input") arg_b = input_value.get("arg_b") or uuid.uuid4().hex raise tornado.gen.Return(input_value.get("arg_a") + arg_b) exposed_thing.set_action_handler(action_name_01, action_handler) yield servient @tornado.gen.coroutine def shutdown(): yield servient.shutdown() tornado.ioloop.IOLoop.current().run_sync(shutdown)
def main(): LOGGER.info('Creating WebSocket server on: {}'.format(WEBSOCKET_PORT)) ws_server = WebsocketServer(port=WEBSOCKET_PORT) LOGGER.info('Creating HTTP server on: {}'.format(HTTP_PORT)) http_server = HTTPServer(port=HTTP_PORT) LOGGER.info( 'Creating servient with TD catalogue on: {}'.format(CATALOGUE_PORT)) servient = Servient(catalogue_port=CATALOGUE_PORT) servient.add_server(ws_server) servient.add_server(http_server) LOGGER.info('Starting servient') wot = yield servient.start() LOGGER.info('Exposing and configuring Thing') # Produce the Thing from Thing Description exposed_thing = wot.produce(json.dumps(TD)) # Initialize the property values yield exposed_thing.properties['allAvailableResources'].write({ 'water': read_from_sensor('water'), 'milk': read_from_sensor('milk'), 'chocolate': read_from_sensor('chocolate'), 'coffeeBeans': read_from_sensor('coffeeBeans'), }) yield exposed_thing.properties['possibleDrinks'].write([ 'espresso', 'americano', 'cappuccino', 'latte', 'hotChocolate', 'hotWater' ]) yield exposed_thing.properties['maintenanceNeeded'].write(False) yield exposed_thing.properties['schedules'].write([]) # # Observe the value of maintenanceNeeded property exposed_thing.properties['maintenanceNeeded'].subscribe( # Notify a "maintainer" when the value has changed # (the notify function here simply logs a message to the console) on_next=lambda data: notify( f'Value changed for an observable property: {data}'), on_completed=notify( 'Subscribed for an observable property: maintenanceNeeded'), on_error=lambda error: notify( f'Error for an observable property maintenanceNeeded: {error}')) # Override a write handler for servedCounter property, # raising maintenanceNeeded flag when the value exceeds 1000 drinks @tornado.gen.coroutine def served_counter_write_handler(value): yield exposed_thing._default_update_property_handler( 'servedCounter', value) if value > 1000: yield exposed_thing.properties['maintenanceNeeded'].write(True) exposed_thing.set_property_write_handler('servedCounter', served_counter_write_handler) # Now initialize the servedCounter property yield exposed_thing.properties['servedCounter'].write( read_from_sensor('servedCounter')) # Set up a handler for makeDrink action async def make_drink_action_handler(params): params = params['input'] if params['input'] else {} # Default values drinkId = 'americano' size = 'm' quantity = 1 # Size quantifiers sizeQuantifiers = {'s': 0.1, 'm': 0.2, 'l': 0.3} # Drink recipes showing the amount of a resource consumed for a particular drink drinkRecipes = { 'espresso': { 'water': 1, 'milk': 0, 'chocolate': 0, 'coffeeBeans': 2, }, 'americano': { 'water': 2, 'milk': 0, 'chocolate': 0, 'coffeeBeans': 2, }, 'cappuccino': { 'water': 1, 'milk': 1, 'chocolate': 0, 'coffeeBeans': 2, }, 'latte': { 'water': 1, 'milk': 2, 'chocolate': 0, 'coffeeBeans': 2, }, 'hotChocolate': { 'water': 0, 'milk': 0, 'chocolate': 1, 'coffeeBeans': 0, }, 'hotWater': { 'water': 1, 'milk': 0, 'chocolate': 0, 'coffeeBeans': 0, }, } # Check if params are provided drinkId = params.get('drinkId', drinkId) size = params.get('size', size) quantity = params.get('quantity', quantity) # Read the current level of allAvailableResources resources = await exposed_thing.read_property('allAvailableResources') # Calculate the new level of resources newResources = resources.copy() newResources['water'] -= math.ceil(quantity * sizeQuantifiers[size] * drinkRecipes[drinkId]['water']) newResources['milk'] -= math.ceil(quantity * sizeQuantifiers[size] * drinkRecipes[drinkId]['milk']) newResources['chocolate'] -= math.ceil( quantity * sizeQuantifiers[size] * drinkRecipes[drinkId]['chocolate']) newResources['coffeeBeans'] -= math.ceil( quantity * sizeQuantifiers[size] * drinkRecipes[drinkId]['coffeeBeans']) # Check if the amount of available resources is sufficient to make a drink for resource, value in six.iteritems(newResources): if value <= 0: # Emit outOfResource event exposed_thing.emit_event( 'outOfResource', f'Low level of {resource}: {resources[resource]}%') return { 'result': False, 'message': f'{resource} level is not sufficient' } # Now store the new level of allAvailableResources and servedCounter await exposed_thing.properties['allAvailableResources'].write( newResources) servedCounter = await exposed_thing.read_property('servedCounter') servedCounter += quantity await exposed_thing.properties['servedCounter'].write(servedCounter) # Finally deliver the drink return {'result': True, 'message': f'Your {drinkId} is in progress!'} exposed_thing.set_action_handler('makeDrink', make_drink_action_handler) # Set up a handler for setSchedule action async def set_schedule_action_handler(params): params = params['input'] if params['input'] else {} # Check if required fields are provided in input if 'time' in params and 'mode' in params: # Use default values for non-required fields if not provided params['drinkId'] = params.get('drinkId', 'americano') params['size'] = params.get('size', 'm') params['quantity'] = params.get('quantity', 1) # Now read the schedules property, add a new schedule to it and then rewrite the schedules property schedules = await exposed_thing.read_property('schedules') schedules.append(params) await exposed_thing.properties['schedules'].write(schedules) return {'result': True, 'message': 'Your schedule has been set!'} return { 'result': False, 'message': 'Please provide all the required parameters: time and mode.' } exposed_thing.set_action_handler('setSchedule', set_schedule_action_handler) exposed_thing.expose() LOGGER.info(f'{TD["title"]} is ready')
def coap_servient(): """Returns a Servient that exposes a CoAP server and one ExposedThing.""" from wotpy.protocols.coap.server import CoAPServer coap_port = find_free_port() the_coap_server = CoAPServer(port=coap_port) servient = Servient(catalogue_port=None) servient.add_server(the_coap_server) @tornado.gen.coroutine def start(): raise tornado.gen.Return((yield servient.start())) wot = tornado.ioloop.IOLoop.current().run_sync(start) property_name_01 = uuid.uuid4().hex action_name_01 = uuid.uuid4().hex event_name_01 = uuid.uuid4().hex td_dict = { "id": uuid.uuid4().urn, "title": uuid.uuid4().hex, "properties": { property_name_01: { "observable": True, "type": "string" } }, "actions": { action_name_01: { "input": { "type": "number" }, "output": { "type": "number" }, } }, "events": { event_name_01: { "type": "string" } }, } td = ThingDescription(td_dict) exposed_thing = wot.produce(td.to_str()) exposed_thing.expose() @tornado.gen.coroutine def action_handler(parameters): input_value = parameters.get("input") raise tornado.gen.Return(int(input_value) * 2) exposed_thing.set_action_handler(action_name_01, action_handler) yield servient @tornado.gen.coroutine def shutdown(): yield servient.shutdown() tornado.ioloop.IOLoop.current().run_sync(shutdown)
def test_consumed_client_protocols_preference(): """The Servient selects different protocol clients to consume Things depending on the protocol choices displayed on the Thing Description.""" servient = Servient(catalogue_port=None) @tornado.gen.coroutine def servient_start(): raise tornado.gen.Return((yield servient.start())) @tornado.gen.coroutine def servient_shutdown(): yield servient.shutdown() http_port = find_free_port() http_server = HTTPServer(port=http_port) servient.add_server(http_server) ws_port = find_free_port() ws_server = WebsocketServer(port=ws_port) servient.add_server(ws_server) client_server_map = { HTTPClient: http_server, WebsocketClient: ws_server } wot = tornado.ioloop.IOLoop.current().run_sync(servient_start) prop_name = uuid.uuid4().hex td_produce = ThingDescription({ "id": uuid.uuid4().urn, "name": uuid.uuid4().hex, "properties": { prop_name: { "observable": True, "type": "string" } } }) exposed_thing = wot.produce(td_produce.to_str()) exposed_thing.expose() td_forms_all = ThingDescription.from_thing(exposed_thing.thing) client_01 = servient.select_client(td_forms_all, prop_name) client_01_class = client_01.__class__ assert client_01_class in six.iterkeys(client_server_map) tornado.ioloop.IOLoop.current().run_sync(servient_shutdown) servient.remove_server(client_server_map[client_01_class].protocol) tornado.ioloop.IOLoop.current().run_sync(servient_start) td_forms_removed = ThingDescription.from_thing(exposed_thing.thing) client_02 = servient.select_client(td_forms_removed, prop_name) client_02_class = client_02.__class__ assert client_02_class != client_01_class assert client_02_class in six.iterkeys(client_server_map) tornado.ioloop.IOLoop.current().run_sync(servient_shutdown)