diff --git a/coderbot/api.py b/coderbot/api.py index 88052aff..be2e9c78 100644 --- a/coderbot/api.py +++ b/coderbot/api.py @@ -27,16 +27,7 @@ BUTTON_PIN = 16 -config = Config.read() -bot = CoderBot.get_instance(motor_trim_factor=float(config.get('move_motor_trim', 1.0)), - motor_max_power=int(config.get('motor_max_power', 100)), - motor_min_power=int(config.get('motor_min_power', 0)), - hw_version=config.get('hardware_version'), - pid_params=(float(config.get('pid_kp', 1.0)), - float(config.get('pid_kd', 0.1)), - float(config.get('pid_ki', 0.01)), - float(config.get('pid_max_speed', 200)), - float(config.get('pid_sample_time', 0.01)))) +bot = CoderBot.get_instance() audio_device = Audio.get_instance() cam = Camera.get_instance() diff --git a/coderbot/audio.py b/coderbot/audio.py index ed1a469e..42a4eeca 100644 --- a/coderbot/audio.py +++ b/coderbot/audio.py @@ -26,7 +26,7 @@ import pyaudio import alsaaudio -from six.moves import queue +import queue # [END import_libraries] # Audio recording parameters diff --git a/coderbot/coderbot.py b/coderbot/coderbot.py index 37877506..89c0de1c 100644 --- a/coderbot/coderbot.py +++ b/coderbot/coderbot.py @@ -157,8 +157,10 @@ def exit(self): s.cancel() @classmethod - def get_instance(cls, motor_trim_factor=1.0, motor_max_power=100, motor_min_power=0, hw_version="5", pid_params=(0.8, 0.1, 0.01, 200, 0.01)): + def get_instance(cls, motor_trim_factor=1.0, motor_max_power=100, motor_min_power=0, hw_version="5", pid_params=(0.8, 0.1, 0.01, 200, 0.01), from_defaults=True): if not cls.the_bot: + if from_defaults: + raise ValueError("incorrect CoderBot initialisation") cls.the_bot = CoderBot(motor_trim_factor=motor_trim_factor, motor_max_power= motor_max_power, motor_min_power=motor_min_power, hw_version=hw_version, pid_params=pid_params) return cls.the_bot @@ -272,5 +274,4 @@ def _cb_button(self, gpio, level, tick): elif tick - self._cb_last_tick[gpio] > elapse: self._cb_last_tick[gpio] = tick logging.info("pushed: %d, %d", level, tick) - cb() - + cb() \ No newline at end of file diff --git a/coderbot/main.py b/coderbot/main.py index edcb53d6..36383240 100644 --- a/coderbot/main.py +++ b/coderbot/main.py @@ -8,7 +8,9 @@ import picamera import connexion -from flask_cors import CORS +from connexion.options import SwaggerUIOptions +from connexion.middleware import MiddlewarePosition +from starlette.middleware.cors import CORSMiddleware from camera import Camera from motion import Motion @@ -22,29 +24,27 @@ # Logging configuration logger = logging.getLogger() logger.setLevel(os.environ.get("LOGLEVEL", "INFO")) -# sh = logging.StreamHandler() -# formatter = logging.Formatter('%(message)s') -# sh.setFormatter(formatter) -# logger.addHandler(sh) ## (Connexion) Flask app configuration # Serve a custom version of the swagger ui (Jinja2 templates) based on the default one # from the folder 'swagger-ui'. Clone the 'swagger-ui' repository inside the backend folder -options = {"swagger_ui": False} -connexionApp = connexion.App(__name__, options=options) - -# Connexion wraps FlaskApp, so app becomes connexionApp.app -app = connexionApp.app -# Access-Control-Allow-Origin -CORS(app) -app.debug = False +swagger_ui_options = SwaggerUIOptions(swagger_ui=True) +app = connexion.App(__name__, swagger_ui_options=swagger_ui_options) +app.add_middleware( + CORSMiddleware, + position=MiddlewarePosition.BEFORE_EXCEPTION, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) app.prog_engine = ProgramEngine.get_instance() ## New API and web application # API v1 is defined in v1.yml and its methods are in api.py -connexionApp.add_api('v1.yml') +app.add_api('v1.yml') def button_pushed(): if app.bot_config.get('button_func') == "startstop": @@ -67,8 +67,16 @@ def run_server(): try: try: app.bot_config = Config.read() - - bot = CoderBot.get_instance() + bot = CoderBot.get_instance(motor_trim_factor=float(app.bot_config.get('move_motor_trim', 1.0)), + motor_max_power=int(app.bot_config.get('motor_max_power', 100)), + motor_min_power=int(app.bot_config.get('motor_min_power', 0)), + hw_version=app.bot_config.get('hardware_version'), + pid_params=(float(app.bot_config.get('pid_kp', 1.0)), + float(app.bot_config.get('pid_kd', 0.1)), + float(app.bot_config.get('pid_ki', 0.01)), + float(app.bot_config.get('pid_max_speed', 200)), + float(app.bot_config.get('pid_sample_time', 0.01))), + from_defaults=False) try: audio_device = Audio.get_instance() @@ -78,6 +86,7 @@ def run_server(): logging.warning("Audio not present") try: + logging.info("starting camera") cam = Camera.get_instance() Motion.get_instance() except picamera.exc.PiCameraError: @@ -97,7 +106,7 @@ def run_server(): remove_doreset_file() - app.run(host="0.0.0.0", port=5000, debug=False, use_reloader=False, threaded=True) + app.run(host="0.0.0.0", port=5000) finally: if cam: cam.exit() @@ -105,4 +114,4 @@ def run_server(): bot.exit() if __name__ == "__main__": - run_server() + run_server() \ No newline at end of file diff --git a/coderbot/program.py b/coderbot/program.py index d76395fd..bc66fc3e 100644 --- a/coderbot/program.py +++ b/coderbot/program.py @@ -87,12 +87,13 @@ def __init__(self): for filename in filenames: if PROGRAM_PREFIX in filename: program_name = filename[len(PROGRAM_PREFIX):-len(PROGRAM_SUFFIX)] - logging.info("adding program %s in path %s as default %r", program_name, dirname, ("default" in dirname)) - with open(os.path.join(dirname, filename), "r") as f: - program_dict = json.load(f) - program_dict["default"] = "default" in dirname - program = Program.from_dict(program_dict) - self.save(program) + if self._programs.search(query.name == program_name) == []: + logging.info("adding program %s in path %s as default %r", program_name, dirname, ("default" in dirname)) + with open(os.path.join(dirname, filename), "r") as f: + program_dict = json.load(f) + program_dict["default"] = "default" in dirname + program = Program.from_dict(program_dict) + self.save(program) @classmethod def get_instance(cls): diff --git a/coderbot/v1.yml b/coderbot/v1.yml index a3c8872f..37142724 100644 --- a/coderbot/v1.yml +++ b/coderbot/v1.yml @@ -170,6 +170,10 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' + tags: - Program management responses: @@ -184,6 +188,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' responses: 200: description: "ok" @@ -200,6 +207,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' requestBody: description: Program object required: true @@ -225,6 +235,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' requestBody: description: Program object required: true @@ -248,6 +261,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' responses: 200: description: "ok" @@ -264,6 +280,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' responses: 200: description: "ok" @@ -304,6 +323,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' - name: default in: query schema: @@ -323,6 +345,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' requestBody: description: Update Activity required: true @@ -346,6 +371,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' responses: 200: description: "ok" @@ -386,6 +414,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' tags: - Music extensions responses: @@ -502,12 +533,13 @@ paths: type: string minLength: 1 maxLength: 256 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' description: text to be "spoken" locale: type: string minLength: 1 maxLength: 2 - pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' + pattern: '^[a-zA-Z]+$' description: locale of text to be "spoken" required: - text @@ -586,6 +618,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' tags: - CNN Models responses: @@ -600,6 +635,9 @@ paths: required: true schema: type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' tags: - CNN Models responses: @@ -679,6 +717,7 @@ components: properties: name: type: string + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' tag: type: string Program: @@ -686,9 +725,9 @@ components: properties: name: type: string - pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' minLength: 1 maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' code: type: string minLength: 1 @@ -709,6 +748,7 @@ components: type: string minLength: 1 maxLength: 128 + pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$' description: type: string minLength: 0 @@ -722,4 +762,4 @@ components: - description - default - stock - + \ No newline at end of file diff --git a/docker/stub/requirements.txt b/docker/stub/requirements.txt index 70ff5c16..d937b268 100644 --- a/docker/stub/requirements.txt +++ b/docker/stub/requirements.txt @@ -1,9 +1,6 @@ # API framework -connexion==2.14.2 -Flask==2.2.5 -Flask-Cors==3.0.10 +connexion[uvicorn,flask,swagger-ui]==3.0.5 tinydb==4.8.0 -Werkzeug==2.2.3 # Misc utils setuptools==69.2.0 diff --git a/docker/stub/start.sh b/docker/stub/start.sh index ff2ed337..f28cc766 100755 --- a/docker/stub/start.sh +++ b/docker/stub/start.sh @@ -2,4 +2,4 @@ export PYTHONPATH=./stub:./test:./coderbot cd /coderbot -python3 coderbot/main.py \ No newline at end of file +python3 coderbot/main.py & python3 stub/wifi/main.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 139d980c..d2085ec9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,6 @@ # API framework -connexion==2.14.2 -Flask==2.2.5 -Flask-Cors==3.0.10 +connexion[uvicorn,flask,swagger-ui]==3.0.5 tinydb==4.8.0 -Werkzeug==2.2.3 # Misc utils setuptools==69.2.0 diff --git a/stub/wifi/api.py b/stub/wifi/api.py new file mode 100644 index 00000000..a6a86e9b --- /dev/null +++ b/stub/wifi/api.py @@ -0,0 +1,19 @@ +import logging + +def list_access_points(): + return {"ssids": [{"ssid": "my_wifi"}]} + +def connection_status(): + return {"wifi": "true", "internet": "true"} + +def connect(): + return "ok" + +def forget(): + return "ok" + +def sset_hotspot_ssid(): + return "ok" + +def set_hotspot_password(): + return "ok" \ No newline at end of file diff --git a/stub/wifi/main.py b/stub/wifi/main.py new file mode 100644 index 00000000..d341cb8f --- /dev/null +++ b/stub/wifi/main.py @@ -0,0 +1,35 @@ +#!/usr/bin/python + +import os +import logging +import logging.handlers +import connexion +from connexion.middleware import MiddlewarePosition +from starlette.middleware.cors import CORSMiddleware + +# Logging configuration +logger = logging.getLogger() +logger.setLevel(os.environ.get("LOGLEVEL", "INFO")) + +## (Connexion) Flask app configuration + +# Serve a custom version of the swagger ui (Jinja2 templates) based on the default one +# from the folder 'swagger-ui'. Clone the 'swagger-ui' repository inside the backend folder + +app = connexion.App(__name__) +app.add_middleware( + CORSMiddleware, + position=MiddlewarePosition.BEFORE_EXCEPTION, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +## New API and web application + +# API v1 is defined in v1.yml and its methods are in api.py +app.add_api('v1.yml') + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=9090) \ No newline at end of file diff --git a/stub/wifi/v1.yml b/stub/wifi/v1.yml new file mode 100644 index 00000000..352871d9 --- /dev/null +++ b/stub/wifi/v1.yml @@ -0,0 +1,64 @@ +openapi: "3.0.0" +info: + version: "1.0" + title: OpenAPI 3.0 definition of WiFi API + +servers: + - url: http://coderbot.local/v1 + +# Paths supported by the server application +paths: + /list_access_points: + get: + operationId: "api.list_access_points" + summary: "list Access Points" + responses: + 200: + description: "ok" + tags: + - Wifi + /connection_status: + get: + operationId: "api.connection_status" + summary: "connection Status" + responses: + 200: + description: "ok" + tags: + - Wifi + /connect: + post: + operationId: "api.connect" + summary: "connect" + responses: + 200: + description: "ok" + tags: + - Wifi + /forget: + post: + operationId: "api.forget" + summary: "forget" + responses: + 200: + description: "ok" + tags: + - Wifi + /sset_hotspot_ssid: + post: + operationId: "api.sset_hotspot_ssid" + summary: "sset_hotspot_ssid" + responses: + 200: + description: "ok" + tags: + - Wifi + /set_hotspot_password: + post: + operationId: "api.set_hotspot_password" + summary: "set_hotspot_password" + responses: + 200: + description: "ok" + tags: + - Wifi \ No newline at end of file
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: