From f6e28bec9dbad3fbbf50d4665e9b65aab6f19852 Mon Sep 17 00:00:00 2001 From: Robert Putt Date: Tue, 2 Jul 2024 15:32:38 +0100 Subject: [PATCH] Add Wingbits Integration --- hw_diag/app.py | 2 + hw_diag/templates/template_hyper.html | 6 ++ hw_diag/templates/wingbits.html | 69 +++++++++++++++++ hw_diag/tests/test_app.py | 4 +- hw_diag/utilities/sdr.py | 19 +++++ hw_diag/views/wingbits.py | 106 ++++++++++++++++++++++++++ 6 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 hw_diag/templates/wingbits.html create mode 100644 hw_diag/utilities/sdr.py create mode 100644 hw_diag/views/wingbits.py diff --git a/hw_diag/app.py b/hw_diag/app.py index 0dc8cdce..6a11eac4 100644 --- a/hw_diag/app.py +++ b/hw_diag/app.py @@ -24,6 +24,7 @@ from hw_diag.views.auth import AUTH from hw_diag.views.myst import MYST from hw_diag.views.ttn import TTN +from hw_diag.views.wingbits import WINGBITS from hw_diag.views.thingsix import THINGSIX from hw_diag.views.backup_restore import BACKUP_RESTORE from hw_diag.utilities.quectel import ensure_quectel_health @@ -192,6 +193,7 @@ def post_request(resp): app.register_blueprint(TTN) app.register_blueprint(THINGSIX) app.register_blueprint(BACKUP_RESTORE) + app.register_blueprint(WINGBITS) app.register_blueprint(DIAGNOSTICS) return app diff --git a/hw_diag/templates/template_hyper.html b/hw_diag/templates/template_hyper.html index d61bb138..fd3fb7f7 100644 --- a/hw_diag/templates/template_hyper.html +++ b/hw_diag/templates/template_hyper.html @@ -132,6 +132,12 @@ MystNodes +
  • + + + Wingbits + +
  • diff --git a/hw_diag/templates/wingbits.html b/hw_diag/templates/wingbits.html new file mode 100644 index 00000000..d45b5cd1 --- /dev/null +++ b/hw_diag/templates/wingbits.html @@ -0,0 +1,69 @@ +{% extends 'template_hyper.html' %} + +{% block title %}Wingbits{% endblock %} + +{% block body %} +
    +
    +

    Wingbits Configuration

    +
    +
    +
    + {% if error %} +
    + {{ error }} +
    +
    + {% endif %} +
    + + + + + + + + + + + + + +
    Node Name + +
    Latitude + +
    Longitude + +
    +
    +
    + +

    +
    +
    +
    +
    +
    + + + + {% if sdr_present %} + + {% else %} + + {% endif %} + +
    SDR Operational
    +
    +
    +
    +
    + {% if diagnostics.last_updated %} +

    Last Updated: {{ diagnostics.last_updated }}

    + {% else %} +

    Last Updated: Never

    + {% endif %} +
    +
    +{% endblock %} diff --git a/hw_diag/tests/test_app.py b/hw_diag/tests/test_app.py index 5eb97157..efb1ef77 100644 --- a/hw_diag/tests/test_app.py +++ b/hw_diag/tests/test_app.py @@ -12,6 +12,7 @@ from hw_diag.views.thingsix import THINGSIX from hw_diag.views.ttn import TTN from hw_diag.views.backup_restore import BACKUP_RESTORE +from hw_diag.views.wingbits import WINGBITS class TestGetApp(unittest.TestCase): @@ -46,7 +47,8 @@ def test_blueprints_registered( call(TTN), call(THINGSIX), call(BACKUP_RESTORE), - call(DIAGNOSTICS) + call(DIAGNOSTICS), + call(WINGBITS) ] mock_register_blueprint.assert_has_calls(calls, any_order=False) diff --git a/hw_diag/utilities/sdr.py b/hw_diag/utilities/sdr.py new file mode 100644 index 00000000..6f788f4d --- /dev/null +++ b/hw_diag/utilities/sdr.py @@ -0,0 +1,19 @@ +import os + + +SDR_SEARCH_STRINGS = [ + "rtl283" +] + + +def detect_sdr(): + found_sdr = False + + for search_string in SDR_SEARCH_STRINGS: + output = os.popen( + '/usr/bin/lsusb | grep -i %s | wc -l' % search_string + ).read().strip() + if int(output) > 0: + found_sdr = True + + return found_sdr diff --git a/hw_diag/views/wingbits.py b/hw_diag/views/wingbits.py new file mode 100644 index 00000000..7064d03b --- /dev/null +++ b/hw_diag/views/wingbits.py @@ -0,0 +1,106 @@ +import logging +import os +import time +import json + +from flask import Blueprint +from flask import render_template +from flask import request + +from hm_pyhelper.logger import get_logger +from hw_diag.utilities.auth import authenticate +from hw_diag.utilities.auth import commercial_fleet_only +from hw_diag.utilities.balena_supervisor import BalenaSupervisor +from hw_diag.utilities.diagnostics import read_diagnostics_file +from hw_diag.utilities.dashboard_registration import claim_miner_deeplink +from hw_diag.utilities.sdr import detect_sdr + + +logging.basicConfig(level=os.environ.get("LOGLEVEL", "DEBUG")) + +LOGGER = get_logger(__name__) +WINGBITS = Blueprint('WINGBITS', __name__) + +WINGBITS_CONFIG_FILE = '/var/nebra/wingbits.json' + + +def get_or_create_wingbits_config(): + data = None + try: + with open(WINGBITS_CONFIG_FILE, 'r') as f: + data = json.load(f) + except FileNotFoundError: + data = { + "node_name": "name-not-set", + "latitude": 0.0, + "longitude": 0.0 + } + with open(WINGBITS_CONFIG_FILE, 'w') as f: + json.dump(data, f) + return data + + +def write_wingbits_config(data): + with open(WINGBITS_CONFIG_FILE, 'w') as f: + json.dump(data, f) + + +@WINGBITS.route('/wingbits') +@authenticate +@commercial_fleet_only +def get_wingbits_dashboard(): + diagnostics = read_diagnostics_file() + claim_deeplink = claim_miner_deeplink() + now = round(time.time()) + sdr_present = detect_sdr() + config = get_or_create_wingbits_config() + + return render_template( + 'wingbits.html', + diagnostics=diagnostics, + claim_deeplink=claim_deeplink, + sdr_present=sdr_present, + config=config, + now=now + ) + + +@WINGBITS.route('/wingbits', methods=['POST']) +@authenticate +@commercial_fleet_only +def update_wingbits_config(): + try: + config = { + "node_name": request.form.get('node_name'), + "longitude": float(request.form.get('longitude')), + "latitude": float(request.form.get('latitude')) + } + except Exception as err: + logging.error("Error updating wingbits config: %s" % str(err)) + + diagnostics = read_diagnostics_file() + claim_deeplink = claim_miner_deeplink() + now = round(time.time()) + sdr_present = detect_sdr() + config = get_or_create_wingbits_config() + + return render_template( + 'wingbits.html', + diagnostics=diagnostics, + claim_deeplink=claim_deeplink, + sdr_present=sdr_present, + config=config, + now=now, + error="Invalid configuration options." + ) + + write_wingbits_config(config) + + supervisor = BalenaSupervisor.new_from_env() + supervisor.reboot() + + return render_template( + 'reconfigure_countdown.html', + seconds=120, + next_url='/' + )