diff --git a/.gitignore b/.gitignore index d3bce17..5ffa4e9 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,7 @@ docs/_build/ # PyBuilder target/ + +# vim swap files +.*.swp +.*.swo diff --git a/INSTALL b/INSTALL deleted file mode 100644 index c7460d9..0000000 --- a/INSTALL +++ /dev/null @@ -1,31 +0,0 @@ -Following tasks need to be done before you can run SHOWtime - -==== -1. Compile port_open.c -==== - -The provided port_open.c program needs to be compiled as an executable into the same directory -as showtime.py, which can be done using the following command -# gcc -o port_open port_open.c - -==== -2. Install required Python dependencies -==== - -SHOWtime and some of the provided tabs require certain Python dependencies in order -to work. The dependencies are listed in the requirements.txt and can be installed using pip -using the following command -# pip install -r requirements.txt - -If pip hasn't been installed, it can be usually be installed using apt-get as such -# apt-get install python-pip - -Some of the dependencies may need the python-dev package in order to be installed -# apt-get install python-dev - -==== -3. Run SHOWtime -==== - -After everything else has been done, you should be able to run SHOWtime simply by calling -# python showtime.py diff --git a/UNLICENSE b/LICENSE.txt similarity index 100% rename from UNLICENSE rename to LICENSE.txt diff --git a/README b/README.rst similarity index 100% rename from README rename to README.rst diff --git a/config.py b/config.py deleted file mode 100644 index f38ed02..0000000 --- a/config.py +++ /dev/null @@ -1,25 +0,0 @@ -from tabs.bitcoin import BitcoinPrice, Bitcoind -from tabs.sysinfo import SystemStats, DiskUsage -from tabs.uptime import WebsiteUptime - -import os - -# Add any tabs you want to be visible here -tabs = [ # Track a running Bitcoin node - #Bitcoind({"host": "http://127.0.0.1:8332", - # "username": "bitcoinrpc", - # # Read the password from a file - # "password": "password" }), - - # A Bitcoin price ticker - #BitcoinPrice(), - - # Displays CPU, RAM usage and uptime - SystemStats(), - - # Displays disk usage - DiskUsage(), - - # Tracks website uptime - WebsiteUptime({"websites": [ {"name": "Google", - "url": "http://google.com"} ] })] \ No newline at end of file diff --git a/config.py.example b/config.py.example deleted file mode 100644 index f38ed02..0000000 --- a/config.py.example +++ /dev/null @@ -1,25 +0,0 @@ -from tabs.bitcoin import BitcoinPrice, Bitcoind -from tabs.sysinfo import SystemStats, DiskUsage -from tabs.uptime import WebsiteUptime - -import os - -# Add any tabs you want to be visible here -tabs = [ # Track a running Bitcoin node - #Bitcoind({"host": "http://127.0.0.1:8332", - # "username": "bitcoinrpc", - # # Read the password from a file - # "password": "password" }), - - # A Bitcoin price ticker - #BitcoinPrice(), - - # Displays CPU, RAM usage and uptime - SystemStats(), - - # Displays disk usage - DiskUsage(), - - # Tracks website uptime - WebsiteUptime({"websites": [ {"name": "Google", - "url": "http://google.com"} ] })] \ No newline at end of file diff --git a/display_image.sh b/display_image.sh deleted file mode 100755 index acea7a9..0000000 --- a/display_image.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -cat temp.raw > /dev/ttyUSB0 diff --git a/__init__.py b/odroidshow/__init__.py similarity index 100% rename from __init__.py rename to odroidshow/__init__.py diff --git a/odroidshow/config.py b/odroidshow/config.py new file mode 100644 index 0000000..e6a6ccf --- /dev/null +++ b/odroidshow/config.py @@ -0,0 +1,30 @@ +from odroidshow.tabs.hello import HelloTab +#from odroidshow.tabs.bitcoin import BitcoinPrice, Bitcoind +#from odroidshow.tabs.sysinfo import SystemStats, DiskUsage +#from odroidshow.tabs.uptime import WebsiteUptime + +import os + +# Add any tabs you want to be visible here +tabs = [ + HelloTab(), + + # Track a running Bitcoin node + #Bitcoind({"host": "http://127.0.0.1:8332", + # "username": "bitcoinrpc", + # # Read the password from a file + # "password": "password" }), + + # A Bitcoin price ticker + #BitcoinPrice(), + + # Displays CPU, RAM usage and uptime + #SystemStats(), + + # Displays disk usage + #DiskUsage(), + + # Tracks website uptime + #WebsiteUptime({"websites": [ {"name": "Google", + # "url": "http://google.com"} ] }) + ] diff --git a/context.py b/odroidshow/context.py similarity index 84% rename from context.py rename to odroidshow/context.py index 7d0090b..2480054 100644 --- a/context.py +++ b/odroidshow/context.py @@ -1,12 +1,14 @@ import time import subprocess +import tempfile import atexit import os import sys +import serial from PIL import Image -from utils import split_string_into_chunks +from odroidshow.utils import split_string_into_chunks sys.path.append(os.path.join(os.path.dirname(__file__), "lib")) @@ -34,7 +36,7 @@ def __init__(self, port_name): self.port_name = port_name self.port = None - self.buffer = unicode("") + self.buffer = "" # Current text size self.text_size = 2 @@ -77,22 +79,13 @@ def open_port(self): """ Opens the serial port for writing """ - self.port = open(self.port_name, "w") - - # Run the port_open executable, which sets attributes necessary - # to input commands correctly - try: - subprocess.call([ "./port_open", self.port_name ]) - except OSError as e: - print "Couldn't execute the port_open executable to set terminal parameters!" - - raise e + self.port = serial.Serial(self.port_name, baudrate=500000) def cleanup(self): """ Closes the serial port """ - self.buffer = unicode("\ec\e[2s\e[1r\r") + self.buffer = "\ec\e[2s\e[1r\r" self.sleep(0.1) self.port.close() @@ -101,10 +94,9 @@ def push_to_serial(self): """ Uploads the current content of the buffer into the screen """ - list = [ "echo", "-ne"] - - list.append(self.buffer) - subprocess.call(list, stdout=self.port) + self.buffer = self.buffer.replace("\\e", "\x1B") + self.port.write(bytes(self.buffer, encoding="ascii")) + self.port.flush() self.buffer = "" return self @@ -114,18 +106,18 @@ def get_columns(self): Returns the amount of columns, depending on the current text size """ if self.orientation == Screen.HORIZONTAL: - return Screen.WIDTH / (self.text_size * 6) + return Screen.WIDTH // (self.text_size * 6) else: - return Screen.HEIGHT / (self.text_size * 6) + return Screen.HEIGHT // (self.text_size * 6) def get_rows(self): """ Returns the amount of rows, depending on the current text size """ if self.orientation == Screen.HORIZONTAL: - return Screen.HEIGHT / (self.text_size * 8) + return Screen.HEIGHT // (self.text_size * 8) else: - return Screen.WIDTH / (self.text_size * 8) + return Screen.WIDTH // (self.text_size * 8) # WRITING FUNCTIONS HERE def fg_color(self, color): @@ -166,16 +158,15 @@ def write(self, text, split=True): """ Prints provided text to screen """ + self.characters_on_line += len(text) if (self.characters_on_line >= self.get_columns()): self.characters_on_line = self.characters_on_line % self.get_columns() - - # If the text is longer than 25 characters or so - # sending it all at once will cause artifacts as - # the serial port can't keep up - # Split the string into chunks to prevent this + + # If the text sends more characters at once than the device can handle, + # artifacts appear. So, split the string into chunks to prevent this. if split: - text_chunks = split_string_into_chunks(text, 25) + text_chunks = split_string_into_chunks(text, 10) for chunk in text_chunks: self.buffer += chunk @@ -265,22 +256,35 @@ def set_rotation(self, rotation): def set_cursor_pos(self, x, y): """ - Set cursor position + Set cursor position (in pixels) """ self.buffer += "\e[%s;%sH" % (str(x), str(y)) self.sleep() return self - + + def set_cursor_loc(self, row, column): + """ + Set cursor position (in text character coordinates) + """ + self.buffer += "\e[%s;%sH" % (str(column * self.text_size * 6), str(row * self.text_size * 6)) + + self.sleep() + + return self + def draw_image(self, img_path, x, y): """ Draw image at the specified position THIS METHOD ISN'T RELIABLE """ + + _, raw_path = tempfile.mkstemp() + # Convert the image subprocess.call([ "ffmpeg", "-y", "-loglevel", "8","-i", img_path, "-vcodec", - "rawvideo", "-f", "rawvideo", "-pix_fmt", "rgb565", "temp.raw" ]) + "rawvideo", "-f", "rawvideo", "-pix_fmt", "rgb565", raw_path ]) image = Image.open(img_path) @@ -292,7 +296,10 @@ def draw_image(self, img_path, x, y): self.sleep(0.05) # Call a script to cat the image data to the serial port, # perhaps we could handle this in Python somehow? - subprocess.call([ "./display_image.sh" ]) + raw_file = open(raw_path, "rb") + raw_data = raw_file.read() + self.port.write(raw_data) + self.port.flush() self.sleep(0.05) # Add a linebreak to prevent glitches when printing text again diff --git a/header.py b/odroidshow/header.py similarity index 90% rename from header.py rename to odroidshow/header.py index b64793d..7430778 100644 --- a/header.py +++ b/odroidshow/header.py @@ -1,4 +1,4 @@ -from context import Screen, ScreenContext +from odroidshow.context import Screen, ScreenContext import time class Header: @@ -29,4 +29,4 @@ def render_header(self, ctx, tab, tab_name, tab_count): # Draw the time ctx.write(empty_line + time_str) - ctx.bg_color(Screen.BLACK) \ No newline at end of file + ctx.bg_color(Screen.BLACK) diff --git a/showtime.py b/odroidshow/showtime.py similarity index 84% rename from showtime.py rename to odroidshow/showtime.py index 25c380a..fa2b8df 100755 --- a/showtime.py +++ b/odroidshow/showtime.py @@ -1,13 +1,8 @@ #!/usr/bin/env python -from context import Screen, ScreenContext -import config - -# Import tabs here -from tabs.bitcoin import Bitcoind, BitcoinPrice -from tabs.sysinfo import SystemStats, DiskUsage - -from header import Header +from odroidshow.context import Screen, ScreenContext +from odroidshow.header import Header +import odroidshow.config as config import atexit import time @@ -37,12 +32,12 @@ atexit.register(ctx.cleanup) # Wait 6 seconds for the screen to boot up before we start uploading anything -ctx.sleep(6).reset_lcd().set_rotation(0) +ctx.sleep(6).reset_lcd().set_rotation(Screen.VERTICAL) # Header header = Header() -print "Started" +print("Started") time_since_tab_change = 0 last_time = time.time() @@ -50,7 +45,7 @@ while True: header.render_header(ctx, current_tab, tabs[current_tab].title, len(tabs)) tabs[current_tab].render_tab(ctx) - + time_since_tab_change += time.time() - last_time last_time = time.time() @@ -64,4 +59,4 @@ # the font size to 4 ctx.set_text_size(4) ctx.erase_rows(1, ctx.get_rows()-1) - ctx.set_text_size(2) \ No newline at end of file + ctx.set_text_size(2) diff --git a/tabs/__init__.py b/odroidshow/tabs/__init__.py similarity index 100% rename from tabs/__init__.py rename to odroidshow/tabs/__init__.py diff --git a/tabs/bitcoin.py b/odroidshow/tabs/bitcoin.py similarity index 99% rename from tabs/bitcoin.py rename to odroidshow/tabs/bitcoin.py index cad5f07..976a944 100644 --- a/tabs/bitcoin.py +++ b/odroidshow/tabs/bitcoin.py @@ -1,5 +1,5 @@ from context import Screen, ScreenContext -from tab import Tab +from tabs.tab import Tab from utils import format_timespan @@ -225,4 +225,4 @@ def render_tab(self, ctx): #ctx.fg_color(Screen.WHITE).write("24hr high").fg_color(Screen.YELLOW).linebreak().write_line("$%.2f" % self.high) #ctx.fg_color(Screen.WHITE).write("24hr low").fg_color(Screen.YELLOW).linebreak().write_line("$%.2f" % self.low) - ctx.set_text_size(2) \ No newline at end of file + ctx.set_text_size(2) diff --git a/odroidshow/tabs/hello.py b/odroidshow/tabs/hello.py new file mode 100644 index 0000000..aa28147 --- /dev/null +++ b/odroidshow/tabs/hello.py @@ -0,0 +1,15 @@ +from odroidshow.context import Screen, ScreenContext +from odroidshow.tabs.tab import Tab + +import time + +class HelloTab(Tab): + def __init__(self): + self.title = "Hello World Tab" + + def render_tab(self, ctx): + + ctx.bg_color(Screen.BLACK) + ctx.fg_color(Screen.WHITE) + + ctx.write_line("Hello, World!") diff --git a/tabs/sysinfo.py b/odroidshow/tabs/sysinfo.py similarity index 94% rename from tabs/sysinfo.py rename to odroidshow/tabs/sysinfo.py index ffaf91d..2155fc6 100644 --- a/tabs/sysinfo.py +++ b/odroidshow/tabs/sysinfo.py @@ -1,17 +1,16 @@ -from context import Screen, ScreenContext -from tab import Tab +from odroidshow.context import Screen, ScreenContext +from odroidshow.tabs.tab import Tab +from odroidshow.utils import format_timespan, get_progress_bar import psutil import time import humanfriendly -from utils import format_timespan, get_progress_bar - class SystemStats(Tab): def __init__(self): self.title = "System stats" - self.cpu_usages = [ 0.5, 0.2, 0.7, 0.1 ] + self.cpu_usages = [0.0] * len(psutil.cpu_percent(percpu=True)) self.used_ram = 12505903 self.total_ram = 20189390 @@ -24,7 +23,7 @@ def __init__(self): def render_tab(self, ctx): # Update system info self.update_sysinfo() - + # Print CPU usage for i in range(0, len(self.cpu_usages)): cpu_usage = self.cpu_usages[i] @@ -115,4 +114,4 @@ def update_disk_usage(self): self.disk_usage[disk_partition.mountpoint] = {"total": disk_usage.total, "used": disk_usage.used} - \ No newline at end of file + diff --git a/tabs/tab.py b/odroidshow/tabs/tab.py similarity index 100% rename from tabs/tab.py rename to odroidshow/tabs/tab.py diff --git a/tabs/uptime.py b/odroidshow/tabs/uptime.py similarity index 98% rename from tabs/uptime.py rename to odroidshow/tabs/uptime.py index 183367d..13a28eb 100644 --- a/tabs/uptime.py +++ b/odroidshow/tabs/uptime.py @@ -1,5 +1,5 @@ from context import Screen, ScreenContext -from tab import Tab +from tabs.tab import Tab import urllib2 import time @@ -60,4 +60,4 @@ def render_tab(self, ctx): if status: ctx.fg_color(Screen.GREEN).write_line("UP").linebreak() else: - ctx.fg_color(Screen.RED).write_line("DOWN for %s" % format_timespan(int(time.time() - self.downtime[website]))).linebreak() \ No newline at end of file + ctx.fg_color(Screen.RED).write_line("DOWN for %s" % format_timespan(int(time.time() - self.downtime[website]))).linebreak() diff --git a/utils.py b/odroidshow/utils.py similarity index 100% rename from utils.py rename to odroidshow/utils.py diff --git a/port_open b/port_open deleted file mode 100755 index b312ba7..0000000 Binary files a/port_open and /dev/null differ diff --git a/port_open.c b/port_open.c deleted file mode 100644 index 1365e9d..0000000 --- a/port_open.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -#include - -#define baudrate B500000 - -int main(int argc, char *argv[]) -{ - if (argc != 2) - { - printf("Serial port wasn't provided!\n"); - return 1; - } - - int usbdev; - struct termios options; - - usbdev = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY); - - if (usbdev == -1) - perror("open_port : Unable to open:"); - - tcgetattr(usbdev, &options); - - cfsetispeed(&options, baudrate); - cfsetospeed(&options, baudrate); - - options.c_cflag |= CS8; - options.c_iflag |= IGNBRK; - options.c_iflag &= ~( BRKINT | ICRNL | IMAXBEL | IXON); - options.c_oflag &= ~( OPOST | ONLCR ); - options.c_lflag &= ~( ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE); - options.c_lflag |= NOFLSH; - options.c_cflag &= ~CRTSCTS; - - tcsetattr(usbdev, TCSANOW, &options); - - return 0; -} diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 2888be8..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -argparse==1.2.1 -humanfriendly==1.14 -psutil==2.1.3 -python-jsonrpc==0.6.1 -Pillow==2.3.0 -httplib2==0.8 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..47acca1 --- /dev/null +++ b/setup.py @@ -0,0 +1,34 @@ +from setuptools import setup, find_packages + +setup( + name='odroidshow', + version='0.0.1', + description='Interface for displaying text and data on ODROID Show2 LCD display board', + url='https://github.com/Matoking/SHOWtime.git', + author='Janne Pulkkinen', + author_email='jannepulk@gmail.com', + license='Unlicense', + packages=['odroidshow'], + python_requires='>=3', + + install_requires=[ + 'argparse', + 'Pillow', + + # Optional for sysinfo tab + #'psutil', + #'humanfriendly', + + # Optional for uptime tab + #'httplib2', + + # Optional for bitcoin tab + #'python-jsonrpc', + ], + + entry_points={ + 'console_scripts': [ + 'odroidshow=odroidshow.showtime', + ], + }, +)