code tidied up, redundant api calls removed and real time brightness and speed controls added
@ -1,3 +1,11 @@
|
||||
MSFT,NFLX,GOOG,TSLA,AAPL,INTC,TXN,HPQ,HOG,LUV,WMT
|
||||
MSFT,NFLX,GOOG,TSLA,AAPL,INTC,TXN,HPQ,HOG,LUV,WMT
|
||||
MSFT,NFLX,GOOG,TSLA,AAPL,INTC,TXN,HPQ,HOG,LUV,WMT
|
||||
MSFT
|
||||
NFLX
|
||||
GOOG
|
||||
TSLA
|
||||
AAPL
|
||||
INTC
|
||||
TXN
|
||||
HPQ
|
||||
HOG
|
||||
LUV
|
||||
WMT
|
||||
|
|
BIN
logos/ADSK.png
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.5 KiB |
BIN
logos/AMZN.png
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
BIN
logos/BABA.png
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
logos/BIDU.png
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
logos/DIS.png
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
logos/KO.png
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 2.7 KiB |
BIN
logos/MMM.png
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
logos/MSFT.png
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
logos/NKE.png
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
logos/PYPL.png
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.6 KiB |
BIN
logos/ROKU.png
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
logos/TSLA.png
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 912 B |
BIN
logos/TWTR.png
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 918 B |
BIN
logos/TXN.png
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.1 KiB |
BIN
logos/W.png
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
BIN
logos/WM.png
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 118 B After Width: | Height: | Size: 1.5 KiB |
BIN
logos/down.png
Before Width: | Height: | Size: 246 B After Width: | Height: | Size: 199 B |
BIN
logos/up.png
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 192 B |
17
server.py
@ -30,7 +30,7 @@ ALLOWED_EXTENSIONS = {'csv', 'png'}
|
||||
#ticker.stdin.write(b'from server')
|
||||
#print(ticker.stdout.readlines())
|
||||
#(output, errs)
|
||||
child = pexpect.spawn("sudo -E python3 stockTicker.py")
|
||||
ticker = pexpect.spawn("sudo -E python3 stockTicker.py")
|
||||
|
||||
#stock_ticker = StockTicker()
|
||||
|
||||
@ -89,10 +89,21 @@ def Speed():
|
||||
global speedTime
|
||||
speedTime = request.form['text']
|
||||
print(speedTime)
|
||||
ticker.sendline(speedTime)
|
||||
global LastCommand
|
||||
LastCommand = 'Change Speed'
|
||||
return hello()
|
||||
|
||||
@app.route("/Brightness", methods=['POST'])
|
||||
def Brightness():
|
||||
global brightness
|
||||
brightness = request.form['text']
|
||||
print(brightness)
|
||||
ticker.sendline(brightness)
|
||||
global LastCommand
|
||||
LastCommand = 'Change Brightness'
|
||||
return hello()
|
||||
|
||||
@app.route("/Ticker", methods=['POST'])
|
||||
def Ticker():
|
||||
if request.method == 'POST':
|
||||
@ -143,7 +154,7 @@ def matrix():
|
||||
#child.sendline('sent from server')
|
||||
|
||||
#process = Popen(["sudo", "-E", "python3", "stockTicker.py"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
child.sendline('S')
|
||||
ticker.sendline('S')
|
||||
|
||||
print('back in flask')
|
||||
|
||||
@ -155,7 +166,7 @@ def matrix():
|
||||
try:
|
||||
#stock_ticker.stopStockTicker()
|
||||
LastCommand = 'Stop display at next checkpoint'
|
||||
child.sendline('K')
|
||||
ticker.sendline('K')
|
||||
except:
|
||||
print("none running")
|
||||
elif "Shutdown the pi" in request.form:
|
||||
|
212
stockTicker.py
@ -31,23 +31,14 @@ class StockTicker():
|
||||
def __init__(self):
|
||||
#Define global resources
|
||||
self.symbols = []
|
||||
#self.ApiKeys = [["b1be6696d54342bdb5c8f59a052854fd","237d183faf984e7eba708a647c55153a","3012b3cb19784ed4be2b02e9907e53cb"],["f835e85df2a74df1904fe6dc53bcc17a","41ec268496744c34a96e3c408081300b","2d566fc71ade46d688713b13730c5219"]]
|
||||
#self.ApiKeys = [["6172b84d1f464ad88b95ed52e4391bce","979f9b8c9ee748fbbb57cb490ccaaf42","593fa818a44144699b75433aafd92d15"]]
|
||||
self.ApiKey = "c24qddqad3ickpckgg80"
|
||||
self.SandboxApiKey = "sandbox_c24qddqad3ickpckgg8g"
|
||||
self.retrievedList = []
|
||||
self.greenORred = (255, 255, 255)
|
||||
self.ListStocks = []
|
||||
self.blank = Image.open('logos/blank.png')
|
||||
self.ApiCalledError = False
|
||||
self.keySwapper = 0
|
||||
self.displayTime = 60
|
||||
self.speedDisplay = 25
|
||||
#self.blank = Image.open('logos/blank.png')
|
||||
self.blank = Image.new('RGB', (0, 32))
|
||||
self.running = True
|
||||
self.Delay = 20
|
||||
self.parseCSV = 0
|
||||
self.symbolLength = 0
|
||||
self.brightness = 1.0
|
||||
self.delay = 0.02
|
||||
|
||||
# Configuration for the matrix
|
||||
options = RGBMatrixOptions()
|
||||
@ -69,7 +60,7 @@ class StockTicker():
|
||||
return image
|
||||
|
||||
|
||||
def SetImage(self, image, offset_x = 0, offset_y = 0, unsafe=False):
|
||||
def setImage(self, image, offset_x = 0, offset_y = 0, unsafe=False):
|
||||
|
||||
|
||||
if (image.mode != "RGB"):
|
||||
@ -96,8 +87,7 @@ class StockTicker():
|
||||
|
||||
self.matrix.SetPixel(x + offset_x, y + offset_y, r*self.brightness, g*self.brightness, b*self.brightness)
|
||||
|
||||
|
||||
def ScrollImage(self, image_file, offset_x = 0, offset_y = 0, delay = 0.05):
|
||||
def scrollImage(self, image_file, offset_x = 0, offset_y = 0):
|
||||
image = self.openImage(image_file)
|
||||
img_width, img_height = image.size
|
||||
|
||||
@ -106,18 +96,16 @@ class StockTicker():
|
||||
while offset_x > -img_width:
|
||||
offset_x -= 1
|
||||
|
||||
self.SetImage(image, offset_x = offset_x, offset_y = offset_y)
|
||||
self.setImage(image, offset_x = offset_x, offset_y = offset_y)
|
||||
time.sleep(delay)
|
||||
|
||||
def ScrollImageTransition(self, image_files, offset_x = 0, offset_y = 0, delay = 0.05):
|
||||
def scrollImageTransition(self, image_files, offset_x = 0, offset_y = 0):
|
||||
# use two image files and switch between them with a seemless transition
|
||||
current_img = 1
|
||||
|
||||
|
||||
#while True:
|
||||
|
||||
|
||||
|
||||
if current_img == 1:
|
||||
image = self.openImage(image_files[0])
|
||||
elif current_img == 2:
|
||||
@ -130,15 +118,26 @@ class StockTicker():
|
||||
|
||||
offset_x -= 1
|
||||
|
||||
self.SetImage(image, offset_x = offset_x, offset_y = offset_y)
|
||||
time.sleep(delay)
|
||||
self.setImage(image, offset_x = offset_x, offset_y = offset_y)
|
||||
time.sleep(self.delay)
|
||||
|
||||
|
||||
try:
|
||||
|
||||
if getInput() == 'K':
|
||||
msg = getInput()
|
||||
if msg == 'K':
|
||||
self.resetMatrix()
|
||||
break
|
||||
|
||||
elif msg == 's':
|
||||
stock_ticker.delay = 0.04
|
||||
elif msg == 'm':
|
||||
stock_ticker.delay = 0.02
|
||||
elif msg == 'f':
|
||||
stock_ticker.delay = 0.01
|
||||
elif msg in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
|
||||
self.brightness = min(1.0, float(msg)/10 + 0.1)
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
sys.stdout.flush()
|
||||
pass
|
||||
@ -150,11 +149,6 @@ class StockTicker():
|
||||
current_img = 1
|
||||
offset_x = 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#Get the logo from file that is the same as ticker name
|
||||
def getLogo(self, Ticker):
|
||||
try:
|
||||
@ -200,12 +194,12 @@ class StockTicker():
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
d.text((4, 0), TICKER, fill=(255, 255, 255), font=font)
|
||||
d.text((4, 13), CURRENT, fill=self.greenORred, font=font)
|
||||
d.text((4, 16), CURRENT, fill=self.greenORred, font=font)
|
||||
|
||||
text_width_current, text_height = self.get_text_dimensions(CURRENT, font)
|
||||
|
||||
img.paste(ARROW, ((text_width_current + 9),18))
|
||||
d.text(((text_width_current+29), 13), CHANGE, fill=self.greenORred, font=font)
|
||||
d.text(((text_width_current+29), 16), CHANGE, fill=self.greenORred, font=font)
|
||||
|
||||
text_width_change, text_height = self.get_text_dimensions(CHANGE, font)
|
||||
|
||||
@ -215,53 +209,37 @@ class StockTicker():
|
||||
return img
|
||||
|
||||
#Stitch the logo & prices picture into one image
|
||||
def stitchImage(self, IMAGES):
|
||||
widths, heights = zip(*(i.size for i in IMAGES))
|
||||
def stitchImage(self, image_list):
|
||||
widths, heights = zip(*(i.size for i in image_list))
|
||||
total_width = sum(widths)
|
||||
max_height = max(heights)
|
||||
new_im = Image.new('RGB', (total_width, max_height))
|
||||
x_offset = 0
|
||||
for im in IMAGES:
|
||||
for im in image_list:
|
||||
new_im.paste(im, (x_offset,0))
|
||||
x_offset += im.size[0]
|
||||
print('width:', im.size[0])
|
||||
return new_im
|
||||
|
||||
|
||||
|
||||
#Get current prices and day prices for the ticker then format the response into an array
|
||||
def getStockPrices(self, SYMBOLS):
|
||||
|
||||
def getStockPrices(self, symbols):
|
||||
apiCalledError = False
|
||||
stock_info = []
|
||||
try:
|
||||
|
||||
symbols_list = SYMBOLS.split(",")
|
||||
quotes = [self.finnhubClient.quote(symbol) for symbol in symbols_list]
|
||||
quotes = [self.finnhubClient.quote(symbol) for symbol in symbols]
|
||||
current_prices = [quote['c'] for quote in quotes]
|
||||
opening_prices = [quote['o'] for quote in quotes]
|
||||
|
||||
for i, symbol in enumerate(symbols_list):
|
||||
self.retrievedList.append([symbol, current_prices[i], opening_prices[i]])
|
||||
for i, symbol in enumerate(symbols):
|
||||
stock_info.append([symbol, current_prices[i], opening_prices[i]])
|
||||
|
||||
print(self.retrievedList)
|
||||
|
||||
'''
|
||||
td = TDClient(apikey=APIKEY)
|
||||
tmin = td.time_series(symbol=SYMBOLS, interval="1min", outputsize='1').as_json()
|
||||
tday = td.time_series(symbol=SYMBOLS, interval="1day", outputsize='2').as_json()
|
||||
for i in range(len(symbols_list)):
|
||||
currentMinPriceList = tmin[symbols_list[i]]
|
||||
currentDayPriceList = list(tday[symbols_list[i]])
|
||||
del currentDayPriceList[0]
|
||||
currentMinPriceClose = tmin[symbols_list[i]][0]['close']
|
||||
currentDayPriceClose = currentDayPriceList[0]['close']
|
||||
self.retrievedList.append([])
|
||||
self.retrievedList[i].append(symbols_list[i])
|
||||
self.retrievedList[i].append(currentMinPriceClose)
|
||||
self.retrievedList[i].append(currentDayPriceClose)
|
||||
'''
|
||||
except Exception as e:
|
||||
print("Could not fetch data - API CALLS REACHED? - Will display old image")
|
||||
print(e)
|
||||
self.ApiCalledError = True
|
||||
apiCalledError = True
|
||||
return stock_info, apiCalledError
|
||||
|
||||
def resetMatrix(self):
|
||||
for x in range(self.matrix.width):
|
||||
@ -273,94 +251,75 @@ class StockTicker():
|
||||
#Connect all the pieces togeather creating 1 long final stock image
|
||||
def getFullStockImage(self):
|
||||
|
||||
self.ApiCalledError = False
|
||||
self.ListStocks = []
|
||||
self.retrievedList = []
|
||||
image_list = []
|
||||
|
||||
start = time.time()
|
||||
print('elapsed time: ' + str((time.time()-start)))
|
||||
j=0
|
||||
while (j<3):
|
||||
stock_info, apiCalledError = self.getStockPrices(self.symbols)
|
||||
|
||||
# NT: this weird API key thing doesnt work, when I hard code the API key in the above function it does
|
||||
self.getStockPrices(self.symbols[(j+self.parseCSV)][0])
|
||||
if (apiCalledError == False):
|
||||
for i in range(len(stock_info)):
|
||||
change = float(stock_info[i][1])-float(stock_info[i][2]) #TEXT
|
||||
ticker = stock_info[i][0] #TEXT
|
||||
current = '%.2f' % float(stock_info[i][1]) #TEXT
|
||||
|
||||
if (self.ApiCalledError == False):
|
||||
j+=1
|
||||
print('elapsed time after api call: ' + str((time.time()-start)))
|
||||
for i in range(len(self.retrievedList)):
|
||||
print('elapsed timethrough loop: ' + str((time.time()-start)))
|
||||
Change = float(self.retrievedList[i][1])-float(self.retrievedList[i][2]) #TEXT
|
||||
Ticker = self.retrievedList[i][0] #TEXT
|
||||
Current = '%.2f' % float(self.retrievedList[i][1]) #TEXT
|
||||
logo = self.getLogo(stock_info[i][0])
|
||||
arrow, change = self.getArrow(change)
|
||||
change = '%.2f' % change
|
||||
midFrame = self.textToImage(ticker, current, change, arrow) #IMAGE THE TEXT
|
||||
|
||||
Logo = self.getLogo(self.retrievedList[i][0])
|
||||
Arrow, Change = self.getArrow(Change)
|
||||
Change = '%.2f' % Change
|
||||
MidFrame = self.textToImage(Ticker, Current, Change, Arrow) #IMAGE THE TEXT
|
||||
stitchedStock = self.stitchImage([logo,midFrame])
|
||||
image_list.append(self.blank)
|
||||
image_list.append(stitchedStock)
|
||||
|
||||
StitchedStock = self.stitchImage([Logo,MidFrame])
|
||||
self.ListStocks.append(self.blank)
|
||||
self.ListStocks.append(StitchedStock)
|
||||
|
||||
self.retrievedList = []
|
||||
else:
|
||||
break
|
||||
|
||||
print('elapsed time loop ended: ' + str((time.time()-start)))
|
||||
if (self.ApiCalledError == False):
|
||||
FinalDisplayImage = self.stitchImage(self.ListStocks)
|
||||
FinalDisplayImage.save('final.ppm')
|
||||
print(self.retrievedList)
|
||||
if (apiCalledError == False):
|
||||
finalDisplayImage = self.stitchImage(image_list)
|
||||
finalDisplayImage.save('final.ppm')
|
||||
|
||||
|
||||
#Send the final stitched image to the display for set amount of time
|
||||
def displayMatrix(self):
|
||||
#os.system("sudo ./demo -D1 final.ppm -t " + str(displayTime) +" -m "+ str(speedDisplay) +" --led-gpio-mapping=adafruit-hat --led-rows=32 --led-cols=256")
|
||||
#os.system("sudo ./demo -D1 final.ppm -t " + str(self.displayTime) +" -m "+ str(self.speedDisplay) +" --led-gpio-mapping=adafruit-hat --led-rows=64 --led-cols=64 --led-slowdown-gpio=4 ")
|
||||
self.ScrollImageTransition(['final.ppm', 'final.ppm'], offset_x = 0, offset_y = 0, delay = 0.02)
|
||||
self.scrollImageTransition(['final.ppm', 'final.ppm'], offset_x = 0, offset_y = 0)
|
||||
#Retrieve symbols from the CSV file
|
||||
def GetSymbols(self):
|
||||
def getSymbols(self):
|
||||
|
||||
self.symbols = []
|
||||
CSV = csv.reader(open('csv/tickers.csv', 'r'))
|
||||
z = 0
|
||||
|
||||
for row in CSV:
|
||||
self.symbols.append([])
|
||||
endLine = str(row[0]+','+row[1]+','+row[2]+','+row[3]+','+row[4]+','+row[5]+','+row[6]+','+row[7]+','+row[8]+','+row[9]+','+row[10])
|
||||
self.symbols[z].append(endLine)
|
||||
z+=1
|
||||
print(row)
|
||||
self.symbols.append(row[0])
|
||||
|
||||
|
||||
print(self.symbols)
|
||||
|
||||
#Main run definition called by server
|
||||
def runStockTicker(self, RUNTIME, DELAY, SPEEDTIME):
|
||||
def runStockTicker(self, runtime, delay, speedtime):
|
||||
|
||||
|
||||
self.getSymbols()
|
||||
|
||||
self.parseCSV = 0
|
||||
self.Delay = DELAY
|
||||
self.displayTime = RUNTIME
|
||||
self.speedDisplay = SPEEDTIME
|
||||
self.GetSymbols()
|
||||
self.symbolLength = (len(self.symbols) - 3)
|
||||
self.GetfullStockImage()
|
||||
self.keySwapper += 1
|
||||
self.running = True
|
||||
self.parseCSV += 3
|
||||
|
||||
|
||||
while (True):
|
||||
if (self.running == True):
|
||||
if (self.keySwapper<=1):
|
||||
if(self.parseCSV <= self.symbolLength):
|
||||
#th = threading.Thread(target=self.displayMatrix)
|
||||
#th.start()
|
||||
self.displayMatrix()
|
||||
time.sleep((int(self.displayTime) - int(self.Delay)))
|
||||
self.GetfullStockImage()
|
||||
self.keySwapper += 1
|
||||
th.join()
|
||||
self.parseCSV += 3
|
||||
else:
|
||||
self.parseCSV = 0
|
||||
else:
|
||||
self.keySwapper = 0
|
||||
|
||||
|
||||
#th = threading.Thread(target=self.displayMatrix)
|
||||
#th.start()
|
||||
self.displayMatrix()
|
||||
time.sleep((int(runtime) - int(delay)))
|
||||
self.getfullStockImage()
|
||||
|
||||
#th.join()
|
||||
|
||||
|
||||
else:
|
||||
break;
|
||||
|
||||
@ -376,9 +335,9 @@ if __name__ == '__main__':
|
||||
|
||||
#print(sys.stdin.readlines())
|
||||
stock_ticker = StockTicker()
|
||||
stock_ticker.GetSymbols()
|
||||
stock_ticker.getFullStockImage()
|
||||
stock_ticker.displayMatrix()
|
||||
stock_ticker.getSymbols()
|
||||
#stock_ticker.getFullStockImage()
|
||||
#stock_ticker.displayMatrix()
|
||||
|
||||
|
||||
while True:
|
||||
@ -388,3 +347,14 @@ if __name__ == '__main__':
|
||||
stock_ticker.displayMatrix()
|
||||
|
||||
|
||||
elif msg == 's':
|
||||
stock_ticker.delay = 0.04
|
||||
elif msg == 'm':
|
||||
stock_ticker.delay = 0.02
|
||||
elif msg == 'f':
|
||||
stock_ticker.delay = 0.01
|
||||
elif msg in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
|
||||
stock_ticker.brightness = min(1.0, float(msg)/10 + 0.1)
|
||||
|
||||
|
||||
|
||||
|
BIN
stockTicker.pyc
@ -23,12 +23,18 @@
|
||||
<input name="text" placeholder="20"style="height:24px">
|
||||
<input type="submit" value="Delay"style="height:30px">
|
||||
</form>
|
||||
<p>Set the speed the image scrolls across the screen (m/s, lower is faster)</p>
|
||||
<p>Set the speed the image scrolls across the screen (s, m of f for slow, medium or fast)</p>
|
||||
<form action="/Speed" method="POST">
|
||||
<input name="text" placeholder="25"style="height:24px">
|
||||
<input name="text" placeholder="m"style="height:24px">
|
||||
<input type="submit" value="Speed"style="height:30px">
|
||||
</form>
|
||||
|
||||
<p>Set the brightness, an integer between 0 and 9, higher is brighter</p>
|
||||
<form action="/Brightness" method="POST">
|
||||
<input name="text" placeholder="9"style="height:24px">
|
||||
<input type="submit" value="Brightness"style="height:30px">
|
||||
</form>
|
||||
|
||||
<br><br>
|
||||
<h3>Tickers to display</h3>
|
||||
<p>To change the tickers displayed please upload a CSV (comma separated value) file with 11 tickers on each row e.g</p>
|
||||
@ -50,8 +56,7 @@
|
||||
<p><input type=file name=file style="height:30px">
|
||||
<input type=submit value=Upload style="height:30px">
|
||||
</form>
|
||||
<p>Logos currently in file:</p>
|
||||
<p>{{ logofiles }}</p>
|
||||
|
||||
|
||||
<br><br>
|
||||
<form action="/matrix" method="POST">
|
||||
@ -61,6 +66,9 @@
|
||||
</form>
|
||||
<br><br>
|
||||
<br><br>
|
||||
|
||||
<p>Logos currently in file:</p>
|
||||
<p>{{ logofiles }}</p>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|