7
\$\begingroup\$

I have been working on this Raspberry Pi project for a while. It uses the Poloniex API to get prices of crypto coins and it shows the prices on multiple OLED displays. The displays use different i2c buses; I am switching between them with a multiplexer.

I am trying to keep the code as organized as possible, but I am not used to writing bigger Python projects as I mainly work with C#. I will probably try to split the program into multiple files later and would need hints where to do it.

from flask import Flask
from flask import render_template
from flask_table import Table, Col

import datetime
import subprocess
import time
import smbus
import Adafruit_SSD1306
import Image
import ImageDraw
import ImageFont
import ImageOps 
import json
import collections
from poloniex import Poloniex
import threading
import logging
# logging.basicConfig(level=logging.INFO)
polo = Poloniex()

app = Flask(__name__)

smb_bus = 1
multiplexer_address = 0x70

@app.route('/')
@app.route('/index')
@app.route('/index.html')
def get_coins(name=None):
    coin_pair = json.dumps(polo.returnCurrencies())
    unsorted_coins = json.loads(coin_pair)
    sorted_coins = collections.OrderedDict(sorted(unsorted_coins.items()))

    #remove delisted and disabled coins
    for keys in sorted_coins: 
        if (sorted_coins[keys]["delisted"] == 1) or (sorted_coins[keys]["disabled"] == 1):
            sorted_coins.pop(keys, None)

    return render_template('index.html', coin=sorted_coins.keys())

@app.route('/info')
@app.route('/info.html')
def info():
    process0 = scan0_output = subprocess.Popen(['i2cdetect -y 0'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    process0.wait()
    process1 = scan1_output = subprocess.Popen(['i2cdetect -y 1'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    process1.wait()

    return render_template('info.html', scan0=process0.stdout.read(), scan1=process1.stdout.read())

class multiplexer():
    def __init__(self):
        self.bus = smbus.SMBus(smb_bus)

    def change_channel(self, channel):  

        if   (channel==0): action = 0x01
        elif (channel==1): action = 0x02
        elif (channel==2): action = 0x04
        elif (channel==3): action = 0x08
        elif (channel==4): action = 0x10
        elif (channel==5): action = 0x20
        elif (channel==6): action = 0x40
        elif (channel==7): action = 0x80
        else : action = 0x00

        self.bus.write_byte_data(multiplexer_address,0x04,action)  #0x04 is the register for switching channels 

    def scanner(self): 
        display_a = {} 

        multiplexer_channel = 0
        while multiplexer_channel != 8:

            self.change_channel(multiplexer_channel)

            try:
                oled_display.initialize()
                oled_display.clear()
                state = 1
            except Exception:
                state = 0

            display_a.update({multiplexer_channel : state}) 
            multiplexer_channel += 1 

        print display_a
        return display_a   

class oled():
    def initialize(self):
        RST = 24
        global disp
        disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, i2c_bus=smb_bus)
        global image
        image = Image.new('1', (disp.width, disp.height))
        global draw
        draw = ImageDraw.Draw(image)

    def clear(self):
        disp.begin()
        disp.clear()
        disp.display()


    def draw_image(self, price, percentage, change, coin_name):
        font = ImageFont.truetype('visitor1.ttf', 22)
        font1 = ImageFont.truetype('visitor1.ttf', 19)

        #Display positioning
        line_positionX = 11
        coin_positionX = line_positionX + 4
        percentageSizeX = font1.getsize(percentage)[0]

        percentagePositionX = disp.width - percentageSizeX

        draw.rectangle((0, 0, disp.width, disp.height), outline=0, fill=0)
        # im = Image.open("template.png")
        # image.paste(im)    
        draw.line((line_positionX, 16, 135, 16), fill=255) #vertical
        draw.line((line_positionX, 0, line_positionX, 35), fill=255) #horizontal

        #draw coin price
        change_x_size = font1.getsize(change)[0]

        #if screen cant draw both CoinChange and CoinChangePercentage show only percentage
        if (change_x_size + percentagePositionX < disp.width - coin_positionX):
            draw.text((coin_positionX , 18), change, font=font1, fill=255) 

        draw.text((coin_positionX, -3), price, font=font, fill=255)  
        draw.text((percentagePositionX, 18), percentage, font=font1, fill=255) 
        # draw.text((18, 18), change, font=font1, fill=255) 

        #draw coin name
        coin_name = coin_name[-3:]

        draw.text((0, -3), coin_name[0], font=font1, fill=255) 
        draw.text((0, 8), coin_name[1], font=font1, fill=255) 
        draw.text((0, 19), coin_name[2], font=font1, fill=255) 

        # Display image.
        disp.image(image)
        disp.display()

    # def initialize(self):
    def exit_screen(self):
        font1 = ImageFont.truetype('visitor1.ttf', 30)
        self.clear()
        time.sleep(2)

        for multiplexer_channel, state in available_displays.items():
            if state == 1: #if display is enabled/working
                act.change_output(multiplexer_channel)
                draw.rectangle([(0,0),(128,32)], fill=0)
                draw.text((0 , 0), "DIED", font=font1, fill=255) 
                disp.image(image)
                disp.display()        

class price():

    def coin(self, pair):
        coin_pair = json.dumps(polo.returnTicker()[pair])
        ticker_json = json.loads(coin_pair)

        if pair == "USDT_BTC":
            priceM.formatDataBTC_USD(ticker_json['last'], ticker_json['percentChange'], ticker_json['low24hr'], pair)

        else:
            priceM.formatData(ticker_json['last'], ticker_json['percentChange'], ticker_json['low24hr'], pair)

    def formatData(self, price, percentage, change, pair):

        change = int((float(price) - float(change)) * 100000000)
        change = str(change)
        percentage = float(percentage)* 100
        percentage = round(percentage, 2)

        percentage = str(percentage) + "%"

        #remove the first 0 from the price
        price = price[1:10]

        oled_display.draw_image(price, percentage, change, pair)     

    def formatDataBTC_USD(self, price, percentage, change, pair):
        change = str(int(float(change) - float(price)))
        price = price[0:7] + "$"
        percentage = str(round(float(percentage)* 100, 2))  + "%"

        oled_display.draw_image(price, percentage, change, pair)

class coin():
    def poloCoins():
        coin_pair = json.dumps(polo.returnCurrencies()[0])
        return render_template('index.html', coin_pair=coin)


class activate_job():

    def change_output(self, channel): 
        multiplex.change_channel(channel)

    def initialize_display(self, channel):
        multiplex.change_channel(channel)
        oled_display.initialize()
        oled_display.clear()

    def ticker_display(self ,coin_pair, channel):
        multiplex.change_channel(channel)
        global priceM
        priceM = price()
        # BTC_DGB
        # USDT_BTC
        priceM.coin(coin_pair)
        print "updated price on display: " + str(channel)
        time.sleep(1)

if __name__ == "__main__":
    oled_display = oled()
    act =  activate_job()
    multiplex = multiplexer()
    displays_for_coins = {3:"BTC_DGB", 6:"BTC_SC"}
    available_displays = multiplex.scanner()
    for multiplexer_channel, state in available_displays.items():
        if state == 1: #if display is enabled/working
            act.change_output(multiplexer_channel)
            act.initialize_display(multiplexer_channel)
    while True:
        for display, coin  in displays_for_coins.items():
            try:
                act.ticker_display(coin, display)

            except:      

                oled_display.exit_screen()
                break
    app.run()
\$\endgroup\$
3
\$\begingroup\$

Well, I shall start from the top.

According to the style guide, PEP 8 imports should be in the following order:

  1. standard library imports

  2. related third party imports local

  3. application/library specific imports

Plus, add a single blank line to separate each group.

So, something like the following:

import subprocess
import threading
import logging
import collections
import json
import time
import datetime

from flask import Flask
from flask import render_template
from flask_table import Table, Col

from poloniex import Poloniex
import smbus
import Adafruit_SSD1306
import Image
import ImageDraw
import ImageFont
import ImageOps

A small thing to note is that the convention for class names -as defined in the aforementioned PEP 8- is that they are CamelCase.

I definitely recommend somehow splitting up your code. You could put it into a package along the lines of the following:

.
my_script.py
my_package
|   +-- __init__.py
|   +-- multiplexer.py
|   +-- display.py
|   +-- currency.py

Your multiplexer class and everything to do with it could go into multiplexer.py. oled() into display.py and the price and coin class could go into currency.py -which I am sure could have a better name .... You might even be able to get by with just putting multiplexer.py, display.py, and currency.py in with myscript.py instead of in a separate package.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.