diff --git a/.github/workflows/build_backend.yml b/.github/workflows/build_backend.yml index 5f458638..0feb5584 100644 --- a/.github/workflows/build_backend.yml +++ b/.github/workflows/build_backend.yml @@ -20,7 +20,7 @@ jobs: #python3 -m unittest test/cnn_test.py 2>&1 | tee test-reports/test_report.txt echo "test complete" - run: | - export PYTHONPATH=./coderbot:./stub:./test + export PYTHONPATH=./stub:./coderbot:./test python3 coderbot/main.py > coderbot.log & sleep 30 apt-get install -y python3-venv @@ -28,11 +28,16 @@ jobs: python3 -m venv schemathesis . schemathesis/bin/activate pip install schemathesis - st run --endpoint '^(?!(\/api\/v1\/video\/stream|\/api\/v1\/control\/move|\/api\/v1\/video\/rec|\/api\/v1\/video\/stop)$).*$' --hypothesis-max-examples=10 --request-timeout=20 http://localhost:5000/api/v1/openapi.json + st run --endpoint 'activities' --hypothesis-max-examples=10 --request-timeout=20 http://localhost:5000/api/v1/openapi.json + st run --endpoint 'media' --hypothesis-max-examples=10 --request-timeout=20 http://localhost:5000/api/v1/openapi.json + st run --endpoint 'control/speak' --hypothesis-max-examples=10 --request-timeout=20 http://localhost:5000/api/v1/openapi.json + st run --endpoint 'control/stop' --hypothesis-max-examples=10 --request-timeout=20 http://localhost:5000/api/v1/openapi.json + st run --endpoint 'music' --hypothesis-max-examples=10 --request-timeout=20 http://localhost:5000/api/v1/openapi.json + st run --endpoint 'programs' --hypothesis-max-examples=10 --request-timeout=20 http://localhost:5000/api/v1/openapi.json echo "openapi test complete" release-backend: - #needs: [test] + needs: [test] runs-on: ubuntu-latest steps: - name: Docker meta diff --git a/coderbot/activity.py b/coderbot/activity.py index 71f71a71..f2864c07 100644 --- a/coderbot/activity.py +++ b/coderbot/activity.py @@ -1,14 +1,12 @@ from tinydb import TinyDB, Query -from tinydb.operations import delete -import json # Programs and Activities databases class Activities(): _instance = None - + @classmethod def get_instance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = Activities() return cls._instance @@ -22,15 +20,14 @@ def load(self, name, default): if len(activities) > 0: return activities[0] elif default is not None: - default_Activities = self.activities.search(self.query.default == True) if len(self.activities.search(self.query.default == True)) > 0: return self.activities.search(self.query.default == True)[0] - else: - return None + return None + return None def save(self, name, activity): # if saved activity is "default", reset existing default activity to "non-default" - if activity.get("default", False) == True: + if activity.get("default", False) is True: self.activities.update({'default': False}) if self.activities.search(self.query.name == name) == []: self.activities.insert(activity) @@ -41,10 +38,9 @@ def delete(self, name): activities = self.activities.search(self.query.name == name) if len(activities) > 0: activity = activities[0] - if activity.get("default", False) == True: + if activity.get("default", False) is True: self.activities.update({'default': True}, self.query.stock == True) self.activities.remove(self.query.name == activity["name"]) def list(self): return self.activities.all() - diff --git a/coderbot/api.py b/coderbot/api.py index 35c3827b..114fdaff 100644 --- a/coderbot/api.py +++ b/coderbot/api.py @@ -28,6 +28,7 @@ from audio import Audio from event import EventManager from coderbotTestUnit import run_test as runCoderbotTestUnit +from balena import Balena BUTTON_PIN = 16 @@ -95,7 +96,7 @@ def get_info(): backend_commit = "undefined" coderbot_version = "undefined" update_status = "ok" - kernel = 'undefined' + device = {} motors = 'undefined' try: @@ -107,11 +108,6 @@ def get_info(): except Exception: pass - try: - kernel = subprocess.check_output(["uname", "-r"]).decode('utf-8').replace('\n', '') - except Exception: - pass - try: encoder = bool(Config.read().get('encoder')) if(encoder): @@ -123,12 +119,16 @@ def get_info(): serial = get_serial() - return {'backend_commit': backend_commit, - 'coderbot_version': coderbot_version, - 'update_status': update_status, - 'kernel': kernel, - 'serial': serial, - 'motors': motors} + try: + device = Baleba.get_instance().device() + except Exception: + pass + return { 'backend_commit': device.get("commit"), + 'coderbot_version': coderbot_version, + 'update_status': device.get("status"), + 'kernel': device.get("os_version"), + 'serial': serial, + 'motors': motors } prog = None prog_engine = ProgramEngine.get_instance() @@ -161,7 +161,8 @@ def turn(body): def takePhoto(): try: cam.photo_take() - Audio.say(config.get("sound_shutter")) + audio_device.say(config.get("sound_shutter")) + return 200 except Exception as e: logging.warning("Error: %s", e) @@ -169,6 +170,7 @@ def recVideo(): try: cam.video_rec() audio_device.say(config.get("sound_shutter")) + return 200 except Exception as e: logging.warning("Error: %s", e) @@ -176,6 +178,7 @@ def stopVideo(): try: cam.video_stop() audio_device.say(config.get("sound_shutter")) + return 200 except Exception as e: logging.warning("Error: %s", e) @@ -184,24 +187,24 @@ def speak(body): locale = body.get("locale", "") logging.debug("say: " + text + " in: " + locale) audio_device.say(text, locale) + return 200 def reset(): - logging.debug("reset bot") - shutil.rmtree("data/*") - bot.restart() + Balena.get_instance().purge() + return 200 def halt(): - logging.debug("shutting down") audio_device.say(what=config.get("sound_stop")) - bot.halt() + Balena.get_instance().shutdown() + return 200 def restart(): - logging.debug("restarting bot") - bot.restart() + Balena.get_instance().restart() def reboot(): - logging.debug("rebooting") - bot.reboot() + audio_device.say(what=config.get("sound_stop")) + Balena.get_instance().reboot() + return 200 def video_stream(a_cam): while True: @@ -287,13 +290,14 @@ def info(): def restoreSettings(): Config.restore() - restart() + return restart() def loadSettings(): return Config.get() def saveSettings(body): Config.write(body) + return 200 def updateFromPackage(): os.system('sudo bash /home/pi/clean-update.sh') diff --git a/coderbot/balena/__init__.py b/coderbot/balena/__init__.py new file mode 100644 index 00000000..5b5c9f9f --- /dev/null +++ b/coderbot/balena/__init__.py @@ -0,0 +1,44 @@ +import os +from urllib.request import urlopen, Request +import json +import logging + +class Balena(): + _instance = None + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = Balena() + return cls._instance + + def __init__(self): + self.supervisor_address = os.environ["BALENA_SUPERVISOR_ADDRESS"] + self.supervisor_key = os.environ["BALENA_SUPERVISOR_API_KEY"] + self.app_id_data = json.dumps({ "appId": os.environ["BALENA_APP_ID"] }).encode("utf-8") + self.headers = { 'Content-Type': 'application/json' } + + def purge(self): + logging.debug("reset bot") + req = Request(f'{self.supervisor_address}/v1/purge?apikey={self.supervisor_key}', data=self.app_id_data, headers=self.headers, method='POST') + return urlopen(req).read() + + def shutdown(self): + logging.debug("shutdown bot") + req = Request(f'{self.supervisor_address}/v1/shutdown?apikey={self.supervisor_key}', headers=self.headers, method='POST') + return urlopen(req).read() + + def restart(self): + logging.debug("restarting bot") + req = Request(f'{self.supervisor_address}/v1/restart?apikey={self.supervisor_key}', data=self.app_id_data, headers=self.headers, method='POST') + return urlopen(req).read() + + def reboot(self): + logging.debug("reboot bot") + req = Request(f'{self.supervisor_address}/v1/reboot?apikey={self.supervisor_key}', headers=self.headers, method='POST') + return urlopen(req).read() + + def device(self): + logging.debug("reboot bot", f'{self.supervisor_address}get?apikey={self.supervisor_key}') + req = Request(f'{self.supervisor_address}/device?apikey={self.supervisor_key}', headers=self.headers, method='GET') + return json.load(urlopen(req)) diff --git a/coderbot/coderbot.py b/coderbot/coderbot.py index b28a1894..79547704 100644 --- a/coderbot/coderbot.py +++ b/coderbot/coderbot.py @@ -23,7 +23,7 @@ import logging import pigpio import sonar -import hw.mpu +from hw import mpu from rotary_encoder.wheelsaxel import WheelsAxel # GPIO @@ -268,13 +268,3 @@ def _cb_button(self, gpio, level, tick): logging.info("pushed: %d, %d", level, tick) cb() - def halt(self): - logging.info("halt requested") - pass - - def restart(self): - sys.exit() - - def reboot(self): - logging.info("reboot requested") - diff --git a/coderbot/hw/__init__.py b/coderbot/hw/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/coderbot/hw/mpu.py b/coderbot/hw/mpu.py index 5f266102..702dab45 100644 --- a/coderbot/hw/mpu.py +++ b/coderbot/hw/mpu.py @@ -1,4 +1,4 @@ -from hw import lsm9ds1 +from . import lsm9ds1 import time class AccelGyroMag: diff --git a/coderbot/main.py b/coderbot/main.py index 2071940e..45161004 100644 --- a/coderbot/main.py +++ b/coderbot/main.py @@ -5,22 +5,19 @@ import os import logging import logging.handlers -import subprocess import picamera import connexion -from flask import (send_from_directory, redirect) - from flask_cors import CORS -from coderbot import CoderBot from camera import Camera from motion import Motion from audio import Audio -from program import ProgramEngine, Program +from program import ProgramEngine from config import Config from cnn.cnn_manager import CNNManager from event import EventManager +from coderbot import CoderBot # Logging configuration logger = logging.getLogger() diff --git a/coderbot/program.py b/coderbot/program.py index 81581c4b..19e5c145 100644 --- a/coderbot/program.py +++ b/coderbot/program.py @@ -34,7 +34,7 @@ import event import music import musicPackages -import hw.atmega328p +from hw.atmega328p import ATMega328 PROGRAM_PATH = "./data/" PROGRAM_PREFIX = "program_" @@ -66,7 +66,7 @@ def get_music(): return music.Music.get_instance(musicPackageManager) def get_atmega(): - return atmega328p.ATMega328.get_instance() + return ATMega328.get_instance() class ProgramEngine: diff --git a/defaults/config.json b/defaults/config.json index 92aa1bdc..a93f77f4 100644 --- a/defaults/config.json +++ b/defaults/config.json @@ -38,7 +38,7 @@ "hw_version":"5", "audio_volume_level":"100", "wifi_mode":"ap", - "wifi_ssid":"coderbot_CHANGEMEATFIRSTRUN", + "wifi_ssid":"coderbot", "wifi_psk":"coderbot", "packages_installed":"", "admin_password":"", diff --git a/docker/Dockerfile b/docker/Dockerfile index d2057c54..f2ee6362 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -64,4 +64,4 @@ ADD docker/scripts/*.sh /tmp/. RUN /tmp/install_generic_cnn_models.sh RUN /tmp/install_lib_firmware.sh -ENTRYPOINT cd /coderbot && python3 coderbot/main.py \ No newline at end of file +ENTRYPOINT cd /coderbot && modprobe i2c-dev && python3 coderbot/main.py diff --git a/stub/balena/__init__.py b/stub/balena/__init__.py new file mode 100644 index 00000000..fe88a02b --- /dev/null +++ b/stub/balena/__init__.py @@ -0,0 +1,26 @@ +class Balena(): + _instance = None + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = Balena() + return cls._instance + + def __init__(self): + pass + + def purge(self): + return "" + + def shutdown(self): + return "" + + def restart(self): + return "" + + def reboot(self): + return "" + + def device(self): + return {}