diff --git a/LICENSE b/LICENSE index 5f662b3..f900c81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) +Copyright (c) 2023 Claudio Maradonna Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/odroid_hc4_oled/__init__.py b/odroid_hc4_oled/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/odroid_hc4_oled/screen.py b/odroid_hc4_oled/screen.py new file mode 100644 index 0000000..e266c30 --- /dev/null +++ b/odroid_hc4_oled/screen.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (c) 2023 Claudio Maradonna + +# PYTHON_ARGCOMPLETE_OK + +from luma.core.interface.serial import i2c +from luma.core.render import canvas +from luma.oled.device import ssd1306, ssd1325, ssd1331, sh1106 +from PIL import ImageFont, ImageDraw, Image + +from time import sleep +import time + +import re +import os +from shutil import which + +from pprint import pprint + +serial = i2c(port=0, address=0x3C) +device = ssd1306(serial, rotate=2) + +s_width = 128 +s_height = 64 + +def zpool_parse_data(data): + parts = re.split(r'(?:\n|^)\s*(\w*):\s*', data.strip(), re.MULTILINE)[1:] + parsed = dict(zip(parts[::2], parts[1::2])) + return { + **parsed, + 'config': zpool_parse_config(parsed.get('config', '')) + } + +def zpool_parse_config(data): + lines = [v.strip().split() for v in data.splitlines() if v.strip()] + if lines: + return [ + dict(zip(lines[0], v)) + for v in lines[1:] + ] + return [] + +def upsc_parse_data(data): + lines = data.strip().split("\n") + result = {} + for line in lines: + key, value = line.split(": ") + result[key] = value + return result + +def zpool_print_status(): + zpool_status = os.popen('zpool status') + zpool_output = zpool_status.read() + + zpool_status_data = zpool_parse_data(zpool_output) + + zpool_list_size = os.popen('zpool list -o name,size,free') + zpool_list_output = zpool_list_size.read() + zpool_list_size_data = zpool_parse_config(zpool_list_output) + + with canvas(device) as draw: + draw.text((0,0), "ZFS ->", fill=1) + + pool_name = zpool_status_data["pool"] + pool_status = "ON" if zpool_status_data["state"] == "ONLINE" else "OFF" + final_pool_name = pool_name + ": " + pool_status + + pool_name_width = draw.textlength(final_pool_name) + draw.text((s_width - pool_name_width, 0), final_pool_name, fill=1) + + starting_height = 0 + for drive in zpool_status_data["config"]: + if drive["NAME"] == pool_name: + for drive in zpool_list_size_data: + if drive["NAME"] == pool_name: + starting_height += 9 + + final_string = drive["FREE"] + "/" + drive["SIZE"] + fs_width = draw.textlength(final_string) + draw.text((s_width - fs_width,starting_height), final_string, fill=1) + continue + + starting_height += 9 + + draw.text((0,starting_height), drive["NAME"], fill=1) + drive_state_width = draw.textlength(drive["STATE"]) + draw.text((s_width - drive_state_width,starting_height), drive["STATE"], fill=1) + + draw.text((0, s_height - 10), zpool_status_data["errors"], fill=1) + +def ups_print_status(): + ups_name_finder = os.popen('upsc -l 127.0.0.1') + ups_name_finder_output = ups_name_finder.read().split("\n") + + ups_name = ups_name_finder_output[0] + + upsc_status = os.popen('upsc ' + ups_name + '@127.0.0.1:3493') + upsc_output = upsc_status.read() + + upsc_status_data = upsc_parse_data(upsc_output) + + with canvas(device) as draw: + draw.text((0,0), "UPS ->", fill=1) + + ups_name_width = draw.textlength(ups_name) + draw.text((s_width - ups_name_width, 0), ups_name, fill=1) + + battery_charge = upsc_status_data["battery.charge"] + + used_width = draw.textlength("[]%") + bar_width = (128 - used_width) + real_width = (int(battery_charge) * bar_width) / 100 + + draw.text((0,9), "[", fill=1) + draw.rectangle((5,13, real_width, 17), fill=1) + draw.text((bar_width, 9), "]%", fill=1) + + draw.text((0, 20), upsc_status_data["device.model"], fill=1) + draw.text((0, 28), upsc_status_data["outlet.power"] + "W", fill=1) + draw.text((0, 36), upsc_status_data["output.voltage"] + "V", fill=1) + +def main(): + if which('zpool') is None: + print("Cannot find `zpool` executable.") + + exit(1) + + if which('upsc') is None: + print("Cannot find `upsc` executable.") + + exit(1) + + start_count = 0 + while True: + device.clear() + + if (start_count % 2) == 0: + zpool_print_status() + else: + ups_print_status() + + start_count += 1 + time.sleep(5) + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..83353c7 --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +from setuptools import setup + +setup( + name = 'odroid-hc4-oled', + version = '0.1', + description = 'ODROID LCD panel script', + author = 'Claudio Maradonna', + author_email = 'claudio@unitoo.pw', + license = 'BSD2', + install_requires = [ + 'luma.core >= 1.17.1', + 'luma.oled >= 3.6.0', + 'luma.lcd >= 2.5.0', + ], + packages = [ + 'odroid_hc4_oled' + ], + entry_points = { + 'console_scripts' : [ + 'odroid_hc4_oled = odroid_hc4_oled.screen:main'] + }, + zip_safe = False)