# Copyright (C) 2021 Neythen Treloar neythen@fintic.io # # This file is part of stockTicker project for justinodunn. # # stockTicker can not be copied and/or distributed without the express # permission of Neythen Treloar from flask import Flask, render_template, request from stockTicker import StockTicker from werkzeug.utils import secure_filename import os import datetime import threading import csv import pexpect import time import json from multiprocessing import Process import subprocess from subprocess import Popen, PIPE import numpy as np import copy import urllib.request import sys #stock_ticker = StockTicker() #print('API CALLER NOT STARTED') #open('log.txt', 'w').close() #wipe logs #api_caller = pexpect.spawn("sudo -E taskset -c 3 python3 api_caller.py") api_caller = pexpect.spawn("sudo -E taskset -c 3 python3 database_caller.py") time.sleep(3) api_caller.sendline('A') #os.system("sudo ./check_update.sh") displaying_screensaver = False uploading = False screensaver_p = None ticker_stopped = False professional = json.load(open('csv/display_settings.json', 'r'))[0] == "Professional" command = 300 tickerList = 0 DelayTime = 20 LastCommand = '' speedTime = 25 #print('save') LOGO_FOLDER = 'logos/' CSV_FOLDER = 'csv/new/' ALLOWED_EXTENSIONS = {'csv', 'png'} ticker = pexpect.spawn("sudo -E python3 stockTicker.py") time.sleep(2) # give the ticker time to initialise system_info = json.load(open('csv/system_info.json')) ticker.sendline('*') # run startup gif by default time.sleep(8) if system_info['first_boot']: # let startup message display ticker.sendline('-') system_info['first_boot'] = False json.dump(system_info, open('csv/system_info.json', 'w')) else: ticker.sendline('A') # run by default def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS def process_file(path, filename): default_csv = csv.writer(open('csv/tickers.csv', 'w+')) new_csv = csv.reader(open((path), 'r')) for row in new_csv: default_csv.writerow(row) class DummyProcess(): def close(self): return True app = Flask(__name__) @app.route("/", methods=['GET', 'POST']) def index(): global command all_features = ['Current Weather','Daily Forecast','News', 'Sports (Upcoming Games)','Sports (Past Games)','Sports (Live Games)', 'Sports (Team Stats)','Custom Images', 'Custom GIFs', 'Custom Messages', 'Stocks', 'Crypto', 'Forex'] global professional feature_settings = json.load(open('csv/display_settings.json', 'r')) print('professional',professional) if not professional: currently_displaying = json.load(open('csv/display_settings.json', 'r'))[1] not_displaying = [f for f in all_features if f not in currently_displaying[0]] elif professional: currently_displaying = json.load(open('csv/display_settings.json', 'r'))[1] not_displaying = [f for f in all_features if f not in currently_displaying[0] and f not in currently_displaying[1]] now = datetime.datetime.now() timeString = now.strftime("%Y-%m-%d %H:%M") logos_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logos') LogoList = os.listdir(logos_path) stocks_settings = json.load(open('csv/stocks_settings.json', 'r')) crypto_settings = json.load(open('csv/crypto_settings.json', 'r')) forex_settings = json.load(open('csv/forex_settings.json', 'r')) current_weather= json.load(open('csv/current_weather.json', 'r')) daily_weather = json.load(open('csv/daily_weather.json', 'r')) news_settings = json.load(open('csv/news_settings.json', 'r')) upcoming_games = json.load(open('csv/upcoming_games.json', 'r')) live_games = json.load(open('csv/live_games.json', 'r')) past_games = json.load(open('csv/past_games.json', 'r')) team_stats = json.load(open('csv/league_tables.json', 'r')) image_settings = json.load(open('csv/image_settings.json', 'r')) GIF_settings = json.load(open('csv/GIF_settings.json', 'r')) message_settings = json.load(open('csv/message_settings.json', 'r')) general_settings = json.load(open('csv/general_settings.json', 'r')) templateData = { 'system_info':system_info, 'currently_displaying': currently_displaying, 'not_displaying': not_displaying, 'stocks_settings': stocks_settings, 'crypto_settings': crypto_settings, 'forex_settings': forex_settings, 'current_weather': current_weather, 'daily_weather': daily_weather, 'news_settings': news_settings, 'upcoming_games': upcoming_games, 'past_games': past_games, 'live_games': live_games, 'team_stats': team_stats, 'image_settings':image_settings, 'GIF_settings':GIF_settings, 'message_settings':message_settings, 'professional':professional, 'general_settings':general_settings } return render_template('index.html', **templateData) def save_displaying(input_settings): #print('save') global professional print('save_displaying_input:', input_settings) all_settings = ['Stocks', 'Crypto', 'Forex', 'Current Weather', 'Daily Forecast', 'News', 'Sports (Upcoming Games)', 'Sports (Past Games)', 'Sports (Live Games)', 'Sports (Team Stats)', 'Custom Images', 'Custom GIFs', 'Custom Messages'] professional = len(input_settings) == 2 if professional: all_settings = ['Stocks', 'Crypto', 'Forex', 'Current Weather', 'News'] positions = [] display_settings = [] if professional: input_settings[0] = [i for i in input_settings[0] if i in all_settings] input_settings[1] = [i for i in input_settings[1] if i in all_settings] s = "Professional" if professional else "Standard" display_settings = [s] + [input_settings] print(display_settings) with open('csv/display_settings.json', 'w+') as f: json.dump(list(display_settings), f) @app.route ("/start", methods = ['PUT', 'POST']) def start(): global displaying_screensaver global ticker global api_caller global professional global ticker_stopped if displaying_screensaver: screensaver_p.close() ticker = pexpect.spawn("sudo -E python3 stockTicker.py") #api_caller = pexpect.spawn("sudo -E taskset -c 3 python3 api_caller.py") api_caller = pexpect.spawn("sudo -E taskset -c 3 python3 database_caller.py") displaying_screensaver = False if ticker_stopped: ticker = pexpect.spawn("sudo -E python3 stockTicker.py") ticker_stopped = False api_caller.sendline('A') ticker.sendline('K') ticker.sendline('A') return index() @app.route("/stop") def stop(): print('stop') global displaying_screensaver global ticker global api_caller global professional global ticker_stopped ticker.sendline('K') if not displaying_screensaver: time.sleep(0.1) # give time for leds to turn off ticker.close() else: screensaver_p.close() if not ticker_stopped: time.sleep(0.1) # give time for leds to turn off ticker.close() ticker_stopped = True if displaying_screensaver: screensaver_p.close() ticker = pexpect.spawn("sudo -E python3 stockTicker.py") #api_caller = pexpect.spawn("sudo -E taskset -c 3 python3 api_caller.py") api_caller = pexpect.spawn("sudo -E taskset -c 3 python3 database_caller.py") displaying_screensaver = False return index() @app.route("/update", methods=['PUT','POST']) def update(): uptodate = os.system('sudo ./check_update.sh') print('python:', uptodate) print(uptodate == 'not up to date') print(uptodate == 'up to date') #os.system("./update.sh") #os.system("sudo reboot now") return index() @app.route("/restart") def restart(): os.system("sudo reboot now") return index() @app.route("/reset") def reset(): print('reset') os.system("sudo ./setup_config_files.sh") return index() @app.route("/save", methods = ['PUT', 'POST', 'GET']) def save(): print('save') global uploading data = str(request.data.decode('utf-8')) input_settings = json.loads(data) print(input_settings) save_displaying(input_settings['displaying']) input_settings= input_settings['feature_settings'] feature = input_settings['feature'] if feature in ['Stocks', 'Crypto', 'Forex']: save_trade_settings(input_settings) elif feature in ['Current Weather', 'Daily Forecast']: save_weather_settings(input_settings) elif feature == 'News': save_news_settings(input_settings) elif 'Sports' in feature: save_sports_settings(input_settings) elif feature in ['Custom GIFs', 'Custom Images']: images = request.files names = list(request.files.keys()) print(names) for name in names: print(name) images[name].save('user_uploads/' +name) save_image_settings(input_settings) elif feature == 'Custom Messages': save_message_settings(input_settings) return index() # saves files uploaded to the webpage for images and GIFs @app.route("/upload", methods = ['PUT', 'POST', 'GET']) def upload(): print('upload') global uploading uploading = True try: images = request.files names = list(request.files.keys()) for name in names: images[name].save('user_uploads/' +name) except Exception as e: #print(e) uploading = False return index() def remove_old_uploads(): #remove old files image_settings = json.load(open('csv/image_settings.json', 'r')) GIF_settings = json.load(open('csv/GIF_settings.json', 'r')) for filename in os.listdir('user_uploads'): if filename not in image_settings['images'] and filename not in GIF_settings['images']: os.remove('user_uploads/'+filename) @app.route("/brightness", methods=['PUT','POST']) def brightness(): global brightness data= request.data.decode('utf-8') settings = json.loads(data) print(settings) brightness =settings['brightness'] ticker.sendline(str(int(brightness)-1)) general_settings = json.load(open('csv/general_settings.json', 'r')) general_settings['brightness'] = int(brightness) json.dump(general_settings, open('csv/general_settings.json', 'w+')) return index() def edit_wpa_sup(country, ssid, pwd): current_wpa = open('/etc/wpa_supplicant/wpa_supplicant.conf') wpa_lines = current_wpa.readlines() wpa_lines[2] = 'country={}\n'.format(country) current_wpa.close() #remove this line to append to end instead of overwriting all networks wpa_lines = wpa_lines[0:3] print(wpa_lines) # create new file from scratch wpa_lines = [] wpa_lines.append('ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n') wpa_lines.append('update_config=1\n') wpa_lines.append('country={}\n'.format(country)) wpa_lines.append('\n') wpa_lines.append('network={\n') wpa_lines.append('\tssid="{}"\n'.format(ssid)) wpa_lines.append('\tpsk="{}"\n'.format(pwd)) wpa_lines.append('}\n') wpa_string = ''.join(wpa_lines) print(wpa_string) current_wpa = open('/etc/wpa_supplicant/wpa_supplicant.conf', 'w+') current_wpa.write(wpa_string) @app.route("/wifi", methods = ['PUT', 'POST', 'GET']) def set_wifi(): data= request.data.decode('utf-8') print(str(data)) settings = json.loads(data) country = settings['country'].upper() ssid = settings['ssid'] pwd = settings['pwd'] general_settings = json.load(open('csv/general_settings.json', 'r')) general_settings['country_code'] = country json.dump(general_settings, open('csv/general_settings.json', 'w+')) edit_wpa_sup(country, ssid, pwd) # resstart netoworking os.system('wpa_cli -i wlan0 reconfigure') return index() def check_internet_connection(host='http://google.com'): try: urllib.request.urlopen(host) #Python 3.x return True except: return False def check_network_connection(): ps = subprocess.Popen(['iwconfig'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) try: output = subprocess.check_output(('grep', 'ESSID'), stdin=ps.stdout) return True except subprocess.CalledProcessError: # grep did not match any lines print("No wireless networks connected") return False def edit_hosts(hostname): current_hosts = open('/etc/hosts') hosts_lines = current_hosts.readlines() print(hosts_lines[5]) hosts_lines[5] = '127.0.1.1 {}'.format(hostname) current_hosts.close() hosts_string = ''.join(hosts_lines) current_hosts = open('/etc/hosts', 'w+') current_hosts.write(hosts_string) @app.route("/hostname", methods = ['PUT', 'POST', 'GET']) def hostname(): data= request.data.decode('utf-8') settings = json.loads(data) hostname = settings['hostname'] print('hostname', hostname) edit_hosts(hostname) os.system("sudo hostnamectl set-hostname {}".format(hostname)) os.system("sudo systemctl restart avahi-daemon") general_settings = json.load(open('csv/general_settings.json', 'r')) general_settings['hostname'] = hostname json.dump(general_settings, open('csv/general_settings.json', 'w+')) return index() @app.route("/saveWeatherAPIKey", methods = ['PUT', 'POST']) def saveWeatherAPIKey(): data= request.data.decode('utf-8') settings = json.loads(data) key = settings['api_key'] print(key) with open('./api_keys.txt') as f: lines = f.readlines() if len(lines) == 1: lines.append(str(key)) elif len(lines) == 2: lines[1] = str(key) print(lines) with open('./api_keys.txt', 'w') as f: for line in lines: f.write(line) return index() @app.route("/screensaver", methods = ['PUT', 'POST']) def screensaver(): global displaying_screensaver global ticker global api_caller global screensaver_p data = str(request.data) if displaying_screensaver: screensaver_p.close() else: api_caller.close() ticker.close() if "Pulsating Colors" in data: screensaver_p = pexpect.spawn("sudo -E python3 ./rpi-rgb-led-matrix/bindings/python/samples/pulsing-colors.py --led-gpio-mapping=adafruit-hat --led-slowdown-gpio=4 -r 32 --led-cols 64 -c 2 -P 1") elif "Rotating Square" in data: screensaver_p = pexpect.spawn("sudo -E python3 ./rpi-rgb-led-matrix/bindings/python/samples/rotating-block-generator.py --led-gpio-mapping=adafruit-hat --led-slowdown-gpio=4 -r 32 --led-cols 64 -c 2 -P 1") elif "Pulsating brightness" in data: screensaver_p = pexpect.spawn("sudo -E python3 ./rpi-rgb-led-matrix/bindings/python/samples/pulsing-brightness.py --led-gpio-mapping=adafruit-hat --led-slowdown-gpio=4 -r 32 --led-cols 64 -c 2 -P 1") elif "Game of Life" in data: screensaver_p = pexpect.spawn("sudo -E python3 game_of_life.py") elif "Sleep" in data: screensaver_p = DummyProcess() else: #default in case user hasnt set one yet screensaver_p = DummyProcess() displaying_screensaver = True return index() def combine_dict(current_settings, input_symbols, current_key): # removes keys not in input from current_settings[current_key] and adds keys not in current from input new_settings = copy.deepcopy(current_settings) new_settings[current_key] = {} current_symbols = list(current_settings[current_key].keys()) # add any stock that arent current in the settings for IS in input_symbols: if IS not in current_symbols: new_settings[current_key][IS] = [] else: new_settings[current_key][IS] = current_settings[current_key][IS] return new_settings def save_trade_settings(input_settings): filename = input_settings['feature'].lower() + '_settings.json' current_settings = json.load(open('csv/' + filename, 'r')) current_settings['speed'] = input_settings['speed'].lower() current_settings['animation'] = input_settings['animation'].lower() current_settings['percent'] = input_settings['percent'] current_settings['point'] = input_settings['point'] current_settings['logos'] = input_settings['logos'] current_settings['title'] = input_settings['title'] current_settings = combine_dict(current_settings, input_settings['symbols'], 'symbols') json.dump(current_settings, open('csv/' + filename, 'w+')) if input_settings['feature'].lower() == 'stocks': api_caller.sendline('s') elif input_settings['feature'].lower() == 'crypto': api_caller.sendline('c') elif input_settings['feature'].lower() == 'forex': api_caller.sendline('f') def save_weather_settings(input_settings): print(input_settings) filename = 'current_weather.json' if input_settings['feature'] == 'Current Weather' else 'daily_weather.json' print(filename) current_settings = json.load(open('csv/' + filename, 'r')) current_settings['speed'] = input_settings['speed'].lower() current_settings['animation'] = input_settings['animation'].lower() current_settings['temp'] = input_settings['temp'].lower() current_settings['wind_speed'] = input_settings['wind_speed'].lower() current_settings['title'] = input_settings['title'] if input_settings['feature'] == 'Daily Forecast': current_settings['current_weather'] = input_settings['current_weather'] ''' locations = {} for key in input_settings['locations']: locations[key] = [] current_settings['locations'] = locations ''' current_settings = combine_dict(current_settings, input_settings['locations'], 'locations') json.dump(current_settings, open('csv/' + filename, 'w+')) api_caller.sendline('w') def save_news_settings(input_settings): filename = 'news_settings.json' current_settings = json.load(open('csv/' + filename, 'r')) print(current_settings) current_settings['speed'] = input_settings['speed'].lower() current_settings['animation'] = input_settings['animation'].lower() current_settings['title'] = input_settings['title'] current_settings['category'] = input_settings['category'] current_settings['country'] = input_settings['country'] current_settings['use_category'] = input_settings['use_category'] current_settings['use_country'] = input_settings['use_country'] current_settings['num_headlines'] = input_settings['num_headlines'] print(current_settings) json.dump(current_settings, open('csv/' + filename, 'w+')) api_caller.sendline('n') def save_sports_settings(input_settings): feature = input_settings['feature'] if feature == 'Sports (Upcoming Games)': filename = 'upcoming_games.json' elif feature == 'Sports (Past Games)': filename = 'past_games.json' elif feature == 'Sports (Live Games)': filename = 'live_games.json' elif feature == 'Sports (Team Stats)': filename = 'league_tables.json' current_settings = json.load(open('csv/' + filename, 'r')) current_settings['speed'] = input_settings['speed'].lower() current_settings['animation'] = input_settings['animation'].lower() current_settings['title'] = input_settings['title'] current_settings['feature'] = input_settings['feature'] current_settings = combine_dict(current_settings, input_settings['leagues'], 'leagues') json.dump(current_settings, open('csv/' + filename, 'w+')) api_caller.sendline('S') # for images and GIFs def save_image_settings(input_settings): filename = 'image_settings.json' if input_settings['feature'] == 'Custom Images' else 'GIF_settings.json' current_settings = input_settings current_settings['speed'] = input_settings['speed'].lower() current_settings['animation'] = input_settings['animation'].lower() del current_settings['feature'] json.dump(current_settings, open('csv/' + filename, 'w+')) remove_old_uploads() def save_message_settings(input_settings): current_settings = json.load(open('csv/message_settings.json', 'r')) new_settings = copy.deepcopy(input_settings) print(current_settings) print() print(input_settings) print() for i,IS in enumerate(input_settings['messages']): # check if this is in current_settings for CS in current_settings['messages']: if IS['name'] == CS['name']: new_settings['messages'][i] = CS print(CS) break print(new_settings) json.dump(new_settings, open('csv/message_settings.json', 'w+')) @app.route("/shutdown") def shutdown(): os.system("sudo shutdown now") return index() @app.route("/matrix") def matrix(): if "Run Stocks" in request.form: ticker.sendline('K') ticker.sendline('S') elif "Run Crypto" in request.form: ticker.sendline('K') ticker.sendline('C') elif "Run Forex" in request.form: ticker.sendline('K') ticker.sendline('F') elif "Run News" in request.form: ticker.sendline('K') ticker.sendline('N') elif "Run Weather" in request.form: ticker.sendline('K') ticker.sendline('W') elif "Run Daily Weather" in request.form: ticker.sendline('K') ticker.sendline('D') elif "Past NHL" in request.form: ticker.sendline('K') ticker.sendline('P') elif "Future NHL" in request.form: ticker.sendline('K') ticker.sendline('l') elif "Live NHL" in request.form: ticker.sendline('K') ticker.sendline('L') elif "Premier league table" in request.form: ticker.sendline('K') ticker.sendline('t') elif "Professional" in request.form: ticker.sendline('K') ticker.sendline('b') elif "All" in request.form: ticker.sendline('K') ticker.sendline('A') elif "Multiple" in request.form: ticker.sendline('K') ticker.sendline('+') elif "Stop Display" in request.form: LastCommand = 'Stop display at next checkpoint' ticker.sendline('K') elif "Shutdown the pi" in request.form: try: os.system("sudo shutdown now") except: print("couldn't shutdown") return index() if __name__ == "__main__": app.run(host='0.0.0.0', port=1024, debug=False) # the debuggger causes flickering #sudo ./demo -D1 final.ppm -t 50 -m 25 --led-gpio-mapping=adafruit-hat --led-rows=32 --led-cols=256 --led-slowdown-gpio=4