diff --git a/demo_opts.py b/demo_opts.py new file mode 100644 index 0000000..d118306 --- /dev/null +++ b/demo_opts.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2014-2022 Richard Hull and contributors +# See LICENSE.rst for details. + +import sys +import logging + +from luma.core import cmdline, error + + +# logging +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)-15s - %(message)s' +) +# ignore PIL debug messages +logging.getLogger('PIL').setLevel(logging.ERROR) + + +def display_settings(device, args): + """ + Display a short summary of the settings. + + :rtype: str + """ + iface = '' + display_types = cmdline.get_display_types() + if args.display not in display_types['emulator']: + iface = f'Interface: {args.interface}\n' + + lib_name = cmdline.get_library_for_display_type(args.display) + if lib_name is not None: + lib_version = cmdline.get_library_version(lib_name) + else: + lib_name = lib_version = 'unknown' + + import luma.core + version = f'luma.{lib_name} {lib_version} (luma.core {luma.core.__version__})' + + return f'Version: {version}\nDisplay: {args.display}\n{iface}Dimensions: {device.width} x {device.height}\n{"-" * 60}' + + +def get_device(actual_args=None): + """ + Create device from command-line arguments and return it. + """ + if actual_args is None: + actual_args = sys.argv[1:] + parser = cmdline.create_parser(description='luma.examples arguments') + args = parser.parse_args(actual_args) + + if args.config: + # load config from file + config = cmdline.load_config(args.config) + args = parser.parse_args(config + actual_args) + + # create device + try: + device = cmdline.create_device(args) + print(display_settings(device, args)) + return device + + except error.Error as e: + parser.error(e) + return None diff --git a/fonts/DejaVuSansMono.ttf b/fonts/DejaVuSansMono.ttf new file mode 100644 index 0000000..041cffc Binary files /dev/null and b/fonts/DejaVuSansMono.ttf differ diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..ab67780 --- /dev/null +++ b/install.sh @@ -0,0 +1,30 @@ +#!/bin/bash +echo +echo "************************************************************************************" +echo "************************************************************************************" +echo "** Install OLED Display for Raspberry Pi" +echo "************************************************************************************" +echo "************************************************************************************" +echo +echo "I2C has to be enabled via raspi-config!" +echo +read -p "Press Enter to continue or Ctrl+C to abort" + +#install necessary packages +sudo apt update +sudo apt install -y i2c-tools +# check connection via +i2cdetect -y 1 +# if 3C is enabled, then connection ok +echo +echo "If 3C is enabled, then I2C connection is ok" +echo +read -p "Press Enter to continue or Ctrl+C to abort" + + +sudo apt install python3-dev libffi-dev libssl-dev libjpeg-dev zlib1g-dev libfreetype6-dev liblcms2-dev libopenjp2-7 libtiff5 -y +sudo apt install python3-rpi.gpio python3-pip -y +sudo apt install git -y + +sudo -H pip3 install luma.oled --break-system-packages +sudo -H pip3 install psutils --break-system-packages diff --git a/sys_info_extended.py b/sys_info_extended.py new file mode 100644 index 0000000..1d1c541 --- /dev/null +++ b/sys_info_extended.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2023 Richard Hull and contributors +# See LICENSE.rst for details. + +""" +Display detailed system information in graph format. +It provides a display of CPU, memory, disk utilization, temperature, IP address and system Uptime. + +Needs psutil (+ dependencies) installed:: + + $ sudo apt-get install python-dev + $ sudo -H pip install psutil +""" + +import time +from pathlib import Path +from datetime import datetime +from demo_opts import get_device +from luma.core.render import canvas +from PIL import ImageFont +import psutil +import subprocess as sp +import socket +from collections import OrderedDict + + +def get_temp(): + temp = float(sp.getoutput("vcgencmd measure_temp").split("=")[1].split("'")[0]) + return temp + + +def get_cpu(): + return psutil.cpu_percent() + + +def get_mem(): + return psutil.virtual_memory().percent + + +def get_disk_usage(): + usage = psutil.disk_usage("/") + return usage.used / usage.total * 100 + + +def get_uptime(): + uptime = ("%s" % (datetime.now() - datetime.fromtimestamp(psutil.boot_time()))).split(".")[0] + return "UpTime: %s" % (uptime) + + +def find_single_ipv4_address(addrs): + for addr in addrs: + if addr.family == socket.AddressFamily.AF_INET: # IPv4 + return addr.address + + +def get_ipv4_address(interface_name=None): + if_addrs = psutil.net_if_addrs() + + if isinstance(interface_name, str) and interface_name in if_addrs: + addrs = if_addrs.get(interface_name) + address = find_single_ipv4_address(addrs) + return address if isinstance(address, str) else "" + else: + if_stats = psutil.net_if_stats() + # remove loopback + if_stats_filtered = {key: if_stats[key] for key, stat in if_stats.items() if "loopback" not in stat.flags} + # sort interfaces by + # 1. Up/Down + # 2. Duplex mode (full: 2, half: 1, unknown: 0) + if_names_sorted = [stat[0] for stat in sorted(if_stats_filtered.items(), key=lambda x: (x[1].isup, x[1].duplex), reverse=True)] + if_addrs_sorted = OrderedDict((key, if_addrs[key]) for key in if_names_sorted if key in if_addrs) + + for _, addrs in if_addrs_sorted.items(): + address = find_single_ipv4_address(addrs) + if isinstance(address, str): + return address + + return "" + + +def get_ip(network_interface_name): + return "IP:%s" % (get_ipv4_address(network_interface_name)) + + +def format_percent(percent): + return "%5.1f" % (percent) + + +def draw_text(draw, margin_x, line_num, text): + draw.text((margin_x, margin_y_line[line_num]), text, font=font_default, fill="white") + + +def draw_bar(draw, line_num, percent): + top_left_y = margin_y_line[line_num] + bar_margin_top + draw.rectangle((margin_x_bar, top_left_y, margin_x_bar + bar_width, top_left_y + bar_height), outline="white") + draw.rectangle((margin_x_bar, top_left_y, margin_x_bar + bar_width * percent / 100, top_left_y + bar_height), fill="white") + + +def draw_bar_full(draw, line_num): + top_left_y = margin_y_line[line_num] + bar_margin_top + draw.rectangle((margin_x_bar, top_left_y, margin_x_bar + bar_width_full, top_left_y + bar_height), fill="white") + draw.text((65, top_left_y - 2), "100 %", font=font_full, fill="black") + + +def stats(device): + with canvas(device) as draw: + temp = get_temp() + draw_text(draw, 0, 0, "Temp") + draw_text(draw, margin_x_figure, 0, "%s'C" % (format_percent(temp))) + + cpu = get_cpu() + draw_text(draw, 0, 1, "CPU") + if cpu < 100: + draw_text(draw, margin_x_figure, 1, "%s %%" % (format_percent(cpu))) + draw_bar(draw, 1, cpu) + else: + draw_bar_full(draw, 1) + + mem = get_mem() + draw_text(draw, 0, 2, "Mem") + if mem < 100: + draw_text(draw, margin_x_figure, 2, "%s %%" % (format_percent(mem))) + draw_bar(draw, 2, mem) + else: + draw_bar_full(draw, 2) + + disk = get_disk_usage() + draw_text(draw, 0, 3, "Disk") + if disk < 100: + draw_text(draw, margin_x_figure, 3, "%s %%" % (format_percent(disk))) + draw_bar(draw, 3, disk) + else: + draw_bar_full(draw, 3) + + if datetime.now().second % (toggle_interval_seconds * 2) < toggle_interval_seconds: + draw_text(draw, 0, 4, get_uptime()) + else: + draw_text(draw, 0, 4, get_ip(network_interface_name)) + + +font_size = 12 +font_size_full = 10 +margin_y_line = [0, 13, 25, 38, 51] +margin_x_figure = 78 +margin_x_bar = 31 +bar_width = 52 +bar_width_full = 95 +bar_height = 8 +bar_margin_top = 3 +toggle_interval_seconds = 4 + + +# None : find suitable IPv4 address among all network interfaces +# or specify the desired interface name as string. +network_interface_name = None + + +device = get_device() +font_default = ImageFont.truetype(str(Path(__file__).resolve().parent.joinpath("fonts", "DejaVuSansMono.ttf")), font_size) +font_full = ImageFont.truetype(str(Path(__file__).resolve().parent.joinpath("fonts", "DejaVuSansMono.ttf")), font_size_full) + + +while True: + stats(device) + time.sleep(0.5)