diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000..5a8b3ef --- /dev/null +++ b/.bazelignore @@ -0,0 +1 @@ +execroot diff --git a/.gitignore b/.gitignore index 66817fe..7c80236 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,8 @@ tmp/ /**/config*.toml /**/config*.yml MODULE.bazel.lock +*.tar.zst +*.zapp +.BUILDINFO +.MTREE +.PKGINFO diff --git a/projects/cabal/src/cabal/__main__.py b/projects/cabal/src/cabal/__main__.py new file mode 100644 index 0000000..316dd6d --- /dev/null +++ b/projects/cabal/src/cabal/__main__.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +import sys +import logging +import socket +import socketserver +from pathlib import Path +from subprocess import check_output +import shlex +import os + +logging.basicConfig(level=logging.INFO) + +CONFIG = None + + +class RequestHandler(socketserver.BaseRequestHandler): + def setup(self) -> None: + logging.info("Start request.") + + def handle(self) -> None: + conn = self.request + while True: + data = conn.recv(1024).decode("utf-8") + + if not data: + break + + data = shlex.split(data) + + logging.info(f"recv: {data!r}") + if command_allowed(data): + resp = check_output() + conn.sendall(resp) + + else: + logging.error("Invalid request %r", data) + + def finish(self) -> None: + logging.info("Finish request.") + + +class Server(socketserver.ThreadingUnixStreamServer): + def server_activate(self) -> None: + logging.info("Server started on %s", self.server_address) + super().server_activate() + + +if __name__ == "__main__": + socket_path = Path(sys.argv[1]) + + if socket_path.exists(): + socket_path.unlink() + + with Server(str(socket_path), RequestHandler) as server: + server.serve_forever() diff --git a/projects/lcdbackpack/BUILD.bazel b/projects/lcdbackpack/BUILD.bazel new file mode 100644 index 0000000..810e416 --- /dev/null +++ b/projects/lcdbackpack/BUILD.bazel @@ -0,0 +1,7 @@ +py_project( + name = "lcdbackpack", + lib_deps = [ + py_requirement("click"), + py_requirement("pyserial"), + ], +) diff --git a/projects/lcdbackpack/src/lcdbackpack/__init__.py b/projects/lcdbackpack/src/lcdbackpack/__init__.py new file mode 100644 index 0000000..fe4eee6 --- /dev/null +++ b/projects/lcdbackpack/src/lcdbackpack/__init__.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python3 + +import os +import serial +import sys +from time import sleep +from typing import Optional + +import click + + +class LcdBackpack: + """ + The LcdBackpack class exposes the commands available on the Adafruit LCD USB/Serial Backpack via simple methods. + """ + + # Quoting from the official firmware implementation - + CHANGESPLASH = 0x40 # COL * ROW chars! + DISPLAY_ON = 0x42 # backlight. 1 argument afterwards, in minutes + AUTOWRAPLINE_ON = 0x43 + AUTOWRAPLINE_OFF = 0x44 + DISPLAY_OFF = 0x46 + SETCURSOR_POSITION = 0x47 # 2 args: col, row + SETCURSOR_HOME = 0x48 + UNDERLINECURSOR_ON = 0x4A + UNDERLINECURSOR_OFF = 0x4B + MOVECURSOR_BACK = 0x4C + MOVECURSOR_FORWARD = 0x4D + CUSTOM_CHARACTER = 0x4E # 9 args: char #, 8 bytes data + SET_CONTRAST = 0x50 # 1 arg + AUTOSCROLL_ON = 0x51 + AUTOSCROLL_OFF = 0x52 + BLOCKCURSOR_ON = 0x53 + BLOCKCURSOR_OFF = 0x54 + GPO_OFF = 0x56 + GPO_ON = 0x57 + CLEAR = 0x58 + SETSAVE_CONTRAST = 0x91 # 1 arg + SET_BRIGHTNESS = 0x99 # 1 arg: scale + SETSAVE_BRIGHTNESS = 0x98 # 1 arg: scale + LOADCUSTOMCHARBANK = 0xC0 # 9 args: char #, 8 bytes data + SAVECUSTOMCHARBANK = 0xC1 # 9 args: char #, 8 bytes data + GPO_START_ONOFF = 0xC3 + RGBBACKLIGHT = 0xD0 # 3 args - R G B + SETSIZE = 0xD1 # 2 args - Cols & Rows + TESTBAUD = 0xD2 # zero args, prints baud rate to uart + STARTL_COMMAND = 0xFE # magic byte indicating a subsequent command list + + def __init__(self, serial_device, baud_rate=115200, rows=16, cols=2): + self._baud_rate = baud_rate + self._serial_device = os.path.realpath(serial_device) + self._ser: Optional[serial.Serial] = None + self._rows = rows + self._cols = cols + + def __enter__(self) -> "LcdBackpack": + """ + Connects to the serial port. + """ + self.connect() + return self + + def __exit__(self, type, value, traceback): + if self.connected: + self.disconnect() + + def connect(self): + """ + Manually open a connection. + """ + self._ser = serial.Serial(self._serial_device, self._baud_rate, timeout=1) + + def disconnect(self): + """ + Closes the serial port connection. + """ + if self._ser is not None and self._ser.is_open: + self._ser.close() + self._ser = None + + @property + def connected(self): + """ + Returns the state of the serial connection. + :return: True if the serial port is open (connected), False otherwise. + """ + return self._ser is not None and self._ser.is_open + + def _write_command(self, command_list): + """ + Writes the given command list to the LCD back pack. + :param command_list: The commands to be written to the LCD back pack. + """ + assert self.connected, "Connection required" + self._ser.write(bytes([LcdBackpack.STARTL_COMMAND] + command_list)) + + def display_on(self): + """ + Switches the LCD backlight on. + """ + self._write_command([LcdBackpack.DISPLAY_ON, 0]) + + def display_off(self): + """ + Switches the LCD backlight off. + """ + self._write_command([LcdBackpack.DISPLAY_OFF]) + + def set_brightness(self, brightness: int): + """ + Sets the brightness of the LCD backlight. + :param brightness: integer value from 0 - 255 + """ + assert 0 <= brightness <= 255 + self._write_command([LcdBackpack.SET_BRIGHTNESS, brightness]) + + def set_contrast(self, contrast: int): + """ + Sets the contrast of the LCD character text. + :param contrast: integer value from 0 - 255 + """ + assert 0 <= contrast <= 255 + self._write_command([LcdBackpack.SET_CONTRAST, contrast]) + + def set_autoscroll(self, auto_scroll: bool): + """ + Sets the autoscrolling capability to the value provided. + :param auto_scroll: true/false + """ + if auto_scroll: + self._write_command([LcdBackpack.AUTOSCROLL_ON]) + else: + self._write_command([LcdBackpack.AUTOSCROLL_OFF]) + + def set_autowrapline(self, auto_wrap: bool): + """ + Sets the autowrapping capability to the value provided. + :param auto_wrap: true/false + """ + if auto_wrap: + self._write_command([LcdBackpack.AUTOWRAPLINE_ON]) + else: + self._write_command([LcdBackpack.AUTOWRAPLINE_OFF]) + + def set_cursor_position(self, column: int, row: int): + """ + Moves the cursor to the provided position. + :param column: integer value for column posititon starting from 1 + :param row: integer value for row position starting from 1 + """ + assert 1 <= column <= 255 + assert 1 <= row <= 255 + self._write_command([LcdBackpack.SETCURSOR_POSITION, column, row]) + + def set_cursor_home(self): + """ + Moves the cursor to the "home" positon: column = 1, row = 1. + """ + self._write_command([LcdBackpack.SETCURSOR_HOME]) + + def cursor_forward(self): + """ + Moves the cursor forward one character. + """ + self._write_command([LcdBackpack.MOVECURSOR_FORWARD]) + + def cursor_back(self): + """ + Moves the cursor backward one character. + """ + self._write_command([LcdBackpack.MOVECURSOR_BACK]) + + def set_underline_cursor(self, underline_cursor: bool): + """ + Enables/disables the underline cursor. + :param underline_cursor: true/false + """ + if underline_cursor: + self._write_command([LcdBackpack.UNDERLINECURSOR_ON]) + else: + self._write_command([LcdBackpack.UNDERLINECURSOR_OFF]) + + def set_block_cursor(self, block_cursor: bool): + """ + Enables/disables the block cursor. + :param block_cursor: + """ + if block_cursor: + self._write_command([LcdBackpack.BLOCKCURSOR_OFF]) + else: + self._write_command([LcdBackpack.BLOCKCURSOR_ON]) + + def set_backlight_rgb(self, red: int, green: int, blue: int): + """ + Sets the RGB LCD backlight to the colour provided by red, green and blue values. + :param red: integer value 0 - 255 + :param green: integer value 0 - 255 + :param blue: integer value 0 - 255 + """ + assert 0 <= red <= 255 + assert 0 <= green <= 255 + assert 0 <= blue <= 255 + self._write_command([LcdBackpack.RGBBACKLIGHT, red, green, blue]) + + def set_backlight_red(self): + """ + Sets the backlight of an RGB LCD display to be red. + """ + self.set_backlight_rgb(0xFF, 0, 0) + + def set_backlight_green(self): + """ + Sets the backlight of an RGB LCD display to be green. + """ + self.set_backlight_rgb(0, 0xFF, 0) + + def set_backlight_blue(self): + """ + Sets the backlight of an RGB LCD display to be blue. + """ + self.set_backlight_rgb(0, 0, 0xFF) + + def set_backlight_white(self): + """ + Sets the backlight of an RGB LCD display to be white. + """ + self.set_backlight_rgb(0xFF, 0xFF, 0xFF) + + def set_lcd_size(self, columns: int, rows: int): + """ + Sets the size of the LCD display (columns, rows). + :param columns: the number of columns + :param rows: the number of rows. + """ + assert 0 <= columns <= 255 + assert 0 <= rows <= 255 + self._write_command([LcdBackpack.SETSIZE, columns, rows]) + + def set_gpio_high(self, gpio: int): + """ + Sets the given GPIO pin on the LCD back pack HIGH (5V). + :param gpio: the GPIO pin to set HIGH (1 - 4) + """ + assert 1 <= gpio <= 4 + self._write_command([LcdBackpack.GPO_ON, gpio]) + + def set_gpio_low(self, gpio: int): + """ + Sets the given GPIO pin on the LCD back pack LOW (5V). + :param gpio: the GPIO pin to set LOW (1 - 4) + """ + assert 1 <= gpio <= 4 + self._write_command([LcdBackpack.GPO_OFF, gpio]) + + def clear(self): + """ + Clears all the characters from the display. + """ + self._write_command([LcdBackpack.CLEAR]) + + def write(self, string): + """ + Writes the given text on the LCD display. + :param string: the text to be written on the display. + """ + if self._ser is None or self._ser.closed: + raise serial.SerialException('Not connected') + + self._ser.write(str.encode(string)) + + def set_splash_screen(self, string): + """ + Sets the LCD splash screen. + :param string: the text to be displayed at LCD start up + :param lcd_chars: the total characters of the LCD display + """ + self._write_command([LcdBackpack.CHANGESPLASH] + list(str.encode(string).ljust(self._cols*self._rows))) + + +@click.group() +@click.option("-d", "--device", "device", default="/dev/ttyACM0") +@click.option("-b", "--baud", "baud", type=int, default=115200) +@click.pass_context +def cli(ctx, device, baud): + ctx.obj = LcdBackpack(device, baud) + + if ctx.invoked_subcommand is None: + ctx.invoke(do_write) + + +@cli.command("on") +@click.pass_context +def do_on(ctx): + with ctx.obj as dev: + dev.display_on() + + +@cli.command("off") +@click.pass_context +def do_off(ctx): + with ctx.obj as dev: + dev.display_off() + + +@cli.command("backlight") +@click.argument("brightness", type=int) +@click.pass_context +def do_backlight(ctx, brightness): + with ctx.obj as dev: + dev.set_brightness(brightness) + + +@cli.command("contrast") +@click.argument("contrast", type=int) +@click.pass_context +def do_contrast(ctx, contrast): + with ctx.obj as dev: + dev.set_contrast(contrast) + + +@cli.command("clear") +@click.pass_context +def do_clear(ctx): + with ctx.obj as dev: + dev.clear() + + +@cli.command("write") +@click.option("-d", "--delay", "delay", type=int, default=100, help="Number of ms to delay between sending lines") +@click.pass_context +def do_write(ctx, delay): + with ctx.obj as dev: + buff = [""] * dev._cols + for line in sys.stdin: + line = line[:dev._rows].strip() + buff = buff[1:] + [line] + + dev.clear() + for l in buff: + dev.write(l) + dev.write("\r") + + sleep(delay/1e3) + + +if __name__ == "__main__": + cli() diff --git a/projects/tickertape/BUILD.bazel b/projects/tickertape/BUILD.bazel new file mode 100644 index 0000000..3e494a3 --- /dev/null +++ b/projects/tickertape/BUILD.bazel @@ -0,0 +1,9 @@ +py_project( + name = "tickertape", + shebang = "/usr/bin/env python3", + main = "src/tickertape/__main__.py", + main_deps = [ + "//projects/lcdbackpack", + py_requirement("click"), + ], +) diff --git a/projects/tickertape/Dockerfile b/projects/tickertape/Dockerfile new file mode 100644 index 0000000..a393ad5 --- /dev/null +++ b/projects/tickertape/Dockerfile @@ -0,0 +1,5 @@ +FROM docker.io/library/python:3.13 + +COPY tickertape.zapp /usr/bin/tickertape + +ENTRYPOINT ["/usr/bin/tickertape"] diff --git a/projects/tickertape/PKGBUILD b/projects/tickertape/PKGBUILD new file mode 100644 index 0000000..3a6e9aa --- /dev/null +++ b/projects/tickertape/PKGBUILD @@ -0,0 +1,55 @@ +pkgname=("tickertape") +pkgver="0.1.0" +pkgrel=1 +epoch=1 +pkgdesc="A LCD ticker for host details" + +arch=("any") + +depends=( + python +) + +source=( + tickertape.service + config.toml +) + +sha256sums=( + "SKIP" + "SKIP" +) + +makedepends=( + python + bazel +) + +# Input file verification +verify() { + true +} + +# Input file patching +prepare() { + true +} + +# Building +build() { + cd .. + bazel build :tickertape.zapp + cp -f $(bazel run --run_under=echo :tickertape.zapp) $srcdir/ +} + +# Post-build verification (tests) +check() { + true +} + +# Emplacing artifacts +package() { + install -Dm755 tickertape.zapp "${pkgdir}"/usr/bin/tickertape + install -Dm644 tickertape.service "${pkgdir}"/usr/lib/systemd/system/tickertape.service + install -Dm644 config.toml "${pkgdir}"/etc/tickertape/config.toml +} diff --git a/projects/tickertape/mkdocker.sh b/projects/tickertape/mkdocker.sh new file mode 100644 index 0000000..78b1840 --- /dev/null +++ b/projects/tickertape/mkdocker.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -eoux -o pipefail + +cd "$(dirname "$(realpath "$0")")" +tmpdir=$(mktemp -d) +target="//projects/tickertape:tickertape.zapp" + +bazel build "${target}" +zapp=$(realpath $(bazel run --run_under=echo "${target}")) + +cp Dockerfile "$tmpdir/" +cp -r "$zapp" "$tmpdir/" + +cd "${tmpdir}" +docker build "$@" -f Dockerfile -t registry.tirefireind.us/arrdem/tickertape:latest . +docker push registry.tirefireind.us/arrdem/tickertape:latest diff --git a/projects/tickertape/pkg/tickertape/usr/bin/tickertape b/projects/tickertape/pkg/tickertape/usr/bin/tickertape new file mode 100755 index 0000000..93669a0 Binary files /dev/null and b/projects/tickertape/pkg/tickertape/usr/bin/tickertape differ diff --git a/projects/tickertape/pkg/tickertape/usr/lib/systemd/system/tickertape.service b/projects/tickertape/pkg/tickertape/usr/lib/systemd/system/tickertape.service new file mode 100644 index 0000000..9449181 --- /dev/null +++ b/projects/tickertape/pkg/tickertape/usr/lib/systemd/system/tickertape.service @@ -0,0 +1,8 @@ +[Unit] +Description=A tickertape for Adafruit LCD displays + +[Service] +ExecStart=/usr/bin/tickertape --config /etc/tickertape/config.toml + +[Install] +WantedBy=multi-user.target diff --git a/projects/tickertape/src/tickertape.service b/projects/tickertape/src/tickertape.service new file mode 120000 index 0000000..6e9e760 --- /dev/null +++ b/projects/tickertape/src/tickertape.service @@ -0,0 +1 @@ +/home/arrdem/Documents/hobby/programming/source/projects/tickertape/tickertape.service \ No newline at end of file diff --git a/projects/tickertape/src/tickertape/__main__.py b/projects/tickertape/src/tickertape/__main__.py new file mode 100644 index 0000000..df398a2 --- /dev/null +++ b/projects/tickertape/src/tickertape/__main__.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 + +from datetime import datetime +import ipaddress +from itertools import islice +import json +import os +from pathlib import Path +import re +from shutil import which +import signal +import socket +from subprocess import check_call, check_output +from time import sleep +import tomllib +from typing import Iterable + +import click +from lcdbackpack import LcdBackpack + + +SPLASH_TEXT = "%-16s\r%-16s" % ("TireFire", " Industries") +DEFAULT_CONFIG = { + "tickertape": { + "splash_text": SPLASH_TEXT, + "clock_rate": 3, + "character_rate": 0.75, + }, + "lcd": { + "path": "/dev/serial/by-id/usb-239a_Adafruit_Industries-if00", + "height": 2, + "width": 16, + "brightness": 16, + }, + "proc": {"path": "/proc"}, + "dev": {"path": "/dev"}, +} + + +def hostname() -> str: + hn = os.getenv("TICKERTAPE_HOSTNAME") or socket.gethostname() + + def _handler(m): + if m.group(2) and len(m.group(2)) > 3: + return f"{m.group(1)}..." + else: + return m.group(0) + + return re.sub("(.{1,13})(.*)", _handler, hn) + + +def defaultaddr( + subnets: Iterable[ipaddress.IPv4Network] = (ipaddress.IPv4Network("10.0.0.0/8"),) +) -> str: + # Get all the IPv4 addresses for this host + addresses = [ + ipaddress.IPv4Address(it) + for it in socket.gethostbyname_ex(socket.gethostname())[-1] + ] + # Search out + addresses = [ + it + for it in addresses + if any(net.network_address <= it <= net.broadcast_address for net in subnets) + ] + + for ip in addresses: + yield f"Addr: {ip}" + + +def render_duration(uptime_seconds): + uptime_seconds = int(uptime_seconds) + + sec = uptime_seconds % 60 + + minutes = (uptime_seconds // 60) % 60 + minutes = f"{minutes}m" if minutes > 0 else "" + + hours = uptime_seconds // 60 // 60 % 24 + hours = f"{hours}h" if hours > 0 else "" + + days = uptime_seconds // 60 // 60 // 24 + days = f"{days}d" if days > 0 else "" + + return f"{days}{hours}{minutes}{sec}s" + + +def uptime(proc: Path): + with open(proc / "uptime", "r") as f: + uptime_seconds = int(float(f.readline().split()[0])) + + yield f"Up: {render_duration(uptime_seconds)}" + + +def load(): + load1, load5, load15 = os.getloadavg() + yield f"Load: 1m {load1:.2f}" + yield f"Load: 5m {load5:.2f}" + yield f"Load: 15m {load15:.2f}" + + +def df( + dirs: Iterable[Path] = ( + Path("/"), + Path("/boot"), + Path("/srv"), + ) +): + for dir in dirs: + if dir.is_dir(): + dev, blocks, used, avail, pct, mtpt = re.split( + r"\s+", check_output(["df", str(dir)]).decode("utf-8").splitlines()[1] + ) + yield f"{dir} usage: {pct}" + + +def zfs(): + if zpool := which("zpool"): + status = json.loads(check_output([zpool, "status", "--json"])) + + for pool_name, pool_stats in status["pools"].items(): + yield f"Zpool {pool_name}:" + yield f" State: {pool_stats['state']}" + yield f" Errors: {pool_stats['error_count']}" + yield f" Used: {pool_stats['vdevs'][pool_name]['alloc_space']}" + yield f" Total: {pool_stats['vdevs'][pool_name]['total_space']}" + last_scan_date = datetime.strptime( + pool_stats["scan_stats"]["end_time"], "%a %b %d %H:%M:%S %p %Z %Y" + ) + delta = datetime.now(tz=last_scan_date.tzinfo) - last_scan_date + yield f" Scanned: {render_duration(delta.total_seconds())} ago" + + +def window(text: str, n=2): + """ + Returns a sliding window (of width n) over data from the iterable. + + s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... + """ + offset = 0 + while offset + n <= len(text): + yield text[offset : offset + n] + offset += 1 + else: + if len(text) < n: + yield text + + +def recursive_merge(dict1, dict2): + for key, value in dict2.items(): + if key in dict1 and isinstance(dict1[key], dict) and isinstance(value, dict): + dict1[key] = recursive_merge(dict1[key], value) + else: + dict1[key] = value + return dict1 + + +@click.option( + "--config", + "config", + type=Path, + help="A configuration file", + default=Path("/etc/tickertape/config.toml"), +) +@click.command() +def main(config: Path): + + # Config loading + config_data = {} + + if config.exists(): + with open(config, "rb") as fp: + config_data: Config = tomllib.load(fp) + + config_data = recursive_merge(config_data, DEFAULT_CONFIG) + + dev = Path(os.getenv("TICKERTAPE_DEVDIR") or config_data["dev"]["path"]) + proc = Path(os.getenv("TICKERTAPE_PROCDIR") or config_data["proc"]["path"]) + + hn = hostname() + + # Functions to iterators over lines (or scrolls ) + lower = [ + defaultaddr, + lambda: uptime(proc), + load, + df, + zfs, + ] + + # seconds between display refreshes + clock_rate = config_data["tickertape"]["clock_rate"] + character_rate = config_data["tickertape"]["character_rate"] + + lcd_width = config_data["lcd"]["width"] + lcd_height = config_data["lcd"]["height"] + + with LcdBackpack(config_data["lcd"]["path"]) as dev: + # Screen setup + dev.set_splash_screen(config_data["tickertape"]["splash_text"]) + dev.set_lcd_size( + lcd_width, + lcd_height, + ) + dev.set_brightness(config_data["lcd"]["brightness"]) + dev.set_autoscroll(False) + + # Display the splashscreen + dev.clear() + dev.set_cursor_position(1, 1) + dev.write(SPLASH_TEXT) + sleep(clock_rate) + + # Set the hostname row + dev.set_cursor_position(1, 1) + dev.clear() + dev.write(hn) + + def sigterm_handler(_signo, _stack_frame): + dev.clear() + exit(0) + + signal.signal(signal.SIGTERM, sigterm_handler) + signal.signal(signal.SIGINT, sigterm_handler) + + while True: + for h in lower: + # FIXME: Support dependency injection or args to the handlers? + for line in h(): + # Lines could be longer than 16 characters and if they are + # we want to shift characters down the line. This is an + # inefficient but idiomatic way to achieve that. + for view in window(line, n=lcd_width): + # Reset the cursor + dev.set_cursor_position(1, 2) + # Lay down exactly 16 characters, space padded to flush any state + dev.write("%-16s" % view) + # Pause between sequential characters + sleep(character_rate) + + else: + # And then wait the clock interval + sleep(clock_rate - character_rate) + + +if __name__ == "__main__": + main() diff --git a/projects/tickertape/tickertape.service b/projects/tickertape/tickertape.service new file mode 100644 index 0000000..9449181 --- /dev/null +++ b/projects/tickertape/tickertape.service @@ -0,0 +1,8 @@ +[Unit] +Description=A tickertape for Adafruit LCD displays + +[Service] +ExecStart=/usr/bin/tickertape --config /etc/tickertape/config.toml + +[Install] +WantedBy=multi-user.target diff --git a/tools/python/pythonshim b/tools/python/pythonshim index 20da612..ec86148 100755 --- a/tools/python/pythonshim +++ b/tools/python/pythonshim @@ -4,7 +4,7 @@ # But ... that's exactly what we want to do. # So this script exists to find a 'compliant' Python install and use that. -PYTHONREV="3.12" +PYTHONREV="3" CMD="python${PYTHONREV}" if [ -x "$(command -v "$CMD")" ]; then diff --git a/tools/python/requirements.in b/tools/python/requirements.in index 4f04805..688d810 100644 --- a/tools/python/requirements.in +++ b/tools/python/requirements.in @@ -56,3 +56,4 @@ yaspin pytimeparse git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d#egg=jaraco.text onepasswordconnectsdk +pyserial diff --git a/tools/python/requirements.lock b/tools/python/requirements.lock deleted file mode 100644 index 5071da1..0000000 --- a/tools/python/requirements.lock +++ /dev/null @@ -1,147 +0,0 @@ -aiohttp==3.9.5 -aiohttp-basicauth==1.0.0 -aiosignal==1.3.1 -aiosql==10.1 -alabaster==0.7.16 -anyio==4.3.0 -async-lru==2.0.4 -attrs==23.2.0 -autocommand==2.2.2 -autoflake==2.3.1 -Babel==2.15.0 -beautifulsoup4==4.12.3 -black==24.4.2 -blinker==1.8.2 -build==1.2.1 -cachetools==5.3.3 -certifi==2024.2.2 -charset-normalizer==3.3.2 -cheroot==10.0.1 -CherryPy==18.8.0 -click==8.1.7 -colored==2.2.4 -commonmark==0.9.1 -coverage==7.5.1 -decorator==5.1.1 -deepmerge==1.1.1 -docutils==0.21.2 -ExifRead==3.0.0 -flake8==7.0.0 -Flask==3.0.3 -frozenlist==1.4.1 -h11==0.14.0 -httpcore==0.16.3 -httpx==0.23.3 -hypothesis==6.102.1 -ibis==3.3.0 -icmplib==3.0.4 -idna==3.7 -imagesize==1.4.1 -inflect==7.2.1 -iniconfig==2.0.0 -isort==5.13.2 -itsdangerous==2.2.0 -jaraco.collections==5.0.1 -jaraco.context==5.3.0 -jaraco.functools==4.0.1 -jaraco.text @ git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d -jedi==0.19.1 -Jinja2==3.1.4 -jsonschema==4.22.0 -jsonschema-path==0.3.2 -jsonschema-specifications==2023.12.1 -lark==1.1.9 -lazy-object-proxy==1.10.0 -libsass==0.23.0 -livereload==2.6.3 -lxml==5.2.2 -Markdown==3.6 -MarkupSafe==2.1.5 -mccabe==0.7.0 -meraki==1.46.0 -mirakuru==2.5.2 -mistune==3.0.2 -more-itertools==10.2.0 -multidict==6.0.5 -mypy-extensions==1.0.0 -octorest==0.4 -onepasswordconnectsdk==1.5.1 -openapi-schema-validator==0.6.2 -openapi-spec-validator==0.7.1 -packaging==24.0 -parso==0.8.4 -pathable==0.4.3 -pathspec==0.12.1 -picobox==4.0.0 -pip-tools==7.4.1 -platformdirs==4.2.1 -pluggy==1.5.0 -port-for==0.7.2 -portend==3.2.0 -prompt-toolkit==3.0.43 -proquint==0.2.1 -psutil==5.9.8 -psycopg==3.1.19 -psycopg2==2.9.9 -pudb==2024.1 -py==1.11.0 -pycodestyle==2.11.1 -pycryptodome==3.20.0 -pyflakes==3.2.0 -Pygments==2.18.0 -pyproject_hooks==1.1.0 -pyrsistent==0.20.0 -pytest==8.2.0 -pytest-cov==5.0.0 -pytest-postgresql==6.0.0 -pytest-pudb==0.7.0 -pytest-timeout==2.3.1 -python-dateutil==2.9.0.post0 -pytimeparse==1.1.8 -pytz==2024.1 -PyYAML==6.0.1 -recommonmark==0.7.1 -redis==5.0.4 -referencing==0.31.1 -requests==2.31.0 -retry==0.9.2 -rfc3339-validator==0.1.4 -rfc3986==1.5.0 -rpds-py==0.18.1 -setuptools==69.5.1 -six==1.16.0 -smbus2==0.4.3 -sniffio==1.3.1 -snowballstemmer==2.2.0 -sortedcontainers==2.4.0 -soupsieve==2.5 -Sphinx==7.3.7 -sphinx_mdinclude==0.6.0 -sphinxcontrib-applehelp==1.0.8 -sphinxcontrib-devhelp==1.0.6 -sphinxcontrib-htmlhelp==2.0.5 -sphinxcontrib-httpdomain==1.8.1 -sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-openapi==0.8.4 -sphinxcontrib-programoutput==0.17 -sphinxcontrib-qthelp==1.0.7 -sphinxcontrib-serializinghtml==1.1.10 -tempora==5.5.1 -termcolor==2.3.0 -toml==0.10.2 -tornado==6.4 -typeguard==4.2.1 -typing_extensions==4.11.0 -unify==0.5 -untokenize==0.1.1 -urllib3==2.2.1 -urwid==2.6.11 -urwid_readline==0.14 -wcwidth==0.2.13 -websocket-client==1.8.0 -Werkzeug==3.0.3 -wheel==0.43.0 -yamllint==1.35.1 -yarl==1.9.4 -yaspin==3.0.2 -zc.lockfile==3.0.post1 diff --git a/tools/python/requirements_lock.txt b/tools/python/requirements_lock.txt index 3e0862b..4e6884f 100644 --- a/tools/python/requirements_lock.txt +++ b/tools/python/requirements_lock.txt @@ -1,144 +1,149 @@ -aiohttp==3.9.3 -aiohttp-basicauth==1.0.0 -aiosignal==1.3.1 -aiosql==9.4 -alabaster==0.7.13 -annotated-types==0.5.0 +aiohappyeyeballs==2.4.4 +aiohttp==3.11.11 +aiohttp_basicauth==1.1.0 +aiosignal==1.3.2 +aiosql==13.0 +alabaster==1.0.0 +anyio==4.7.0 async-lru==2.0.4 -async-timeout==4.0.2 -attrs==23.2.0 +attrs==24.3.0 autocommand==2.2.2 -autoflake==2.2.1 -Babel==2.12.1 +autoflake==2.3.1 +babel==2.16.0 beautifulsoup4==4.12.3 -black==24.1.1 -blinker==1.6.2 -build==0.10.0 -cachetools==5.3.2 -certifi==2023.7.22 -charset-normalizer==3.2.0 -cheroot==10.0.0 +black==24.10.0 +blinker==1.9.0 +build==1.2.2.post1 +cachetools==5.5.0 +certifi==2024.12.14 +charset-normalizer==3.4.1 +cheroot==10.0.1 CherryPy==18.8.0 -click==8.1.7 +click==8.1.8 colored==2.2.4 commonmark==0.9.1 -coverage==7.2.7 +coverage==7.6.9 decorator==5.1.1 -deepmerge==1.1.0 -docutils==0.20.1 +deepmerge==2.0 +docutils==0.21.2 ExifRead==3.0.0 -flake8==7.0.0 -Flask==3.0.2 -frozenlist==1.4.0 -hypothesis==6.98.2 +flake8==7.1.1 +Flask==3.1.0 +frozenlist==1.5.0 +h11==0.14.0 +httpcore==0.16.3 +httpx==0.23.3 +hypothesis==6.123.1 ibis==3.3.0 icmplib==3.0.4 -idna==3.4 +idna==3.10 imagesize==1.4.1 -inflect==7.0.0 +inflect==7.4.0 iniconfig==2.0.0 isort==5.13.2 -itsdangerous==2.1.2 -jaraco.collections==4.3.0 -jaraco.context==4.3.0 -jaraco.functools==3.8.0 +itsdangerous==2.2.0 +jaraco.collections==5.1.0 +jaraco.context==6.0.1 +jaraco.functools==4.1.0 jaraco.text @ git+https://github.com/arrdem/jaraco.text.git@0dd8d0b25a93c3fad896f3a92d11caff61ff273d -jedi==0.18.2 -Jinja2==3.1.3 -jsonschema==4.18.4 -jsonschema-path==0.3.2 -jsonschema-spec==0.2.3 -jsonschema-specifications==2023.7.1 -lark==1.1.9 -lazy-object-proxy==1.9.0 +jedi==0.19.2 +Jinja2==3.1.5 +jsonschema==4.23.0 +jsonschema-path==0.3.3 +jsonschema-specifications==2023.12.1 +lark==1.2.2 +lazy-object-proxy==1.10.0 libsass==0.23.0 -livereload==2.6.3 -lxml==5.1.0 -Markdown==3.5.2 -MarkupSafe==2.1.3 +livereload==2.7.1 +lxml==5.3.0 +Markdown==3.7 +MarkupSafe==3.0.2 mccabe==0.7.0 -meraki==1.42.0 -mirakuru==2.5.1 -mistune==2.0.5 -more-itertools==10.0.0 -multidict==6.0.4 +meraki==1.53.0 +mirakuru==2.5.3 +mistune==3.0.2 +more-itertools==10.5.0 +multidict==6.1.0 mypy-extensions==1.0.0 octorest==0.4 -openapi-schema-validator==0.6.0 +onepasswordconnectsdk==1.5.1 +openapi-schema-validator==0.6.2 openapi-spec-validator==0.7.1 -packaging==23.1 -parso==0.8.3 +packaging==24.2 +parso==0.8.4 pathable==0.4.3 -pathspec==0.11.1 -picobox==3.0.0 -pip==23.1.2 -pip-tools==7.3.0 -platformdirs==3.9.1 -pluggy==1.2.0 -port-for==0.7.1 +pathspec==0.12.1 +picobox==4.0.0 +pip-tools==7.4.1 +platformdirs==4.3.6 +pluggy==1.5.0 +port-for==0.7.4 portend==3.2.0 -prompt-toolkit==3.0.43 +prompt_toolkit==3.0.48 +propcache==0.2.1 proquint==0.2.1 -psutil==5.9.5 -psycopg==3.1.9 -psycopg2==2.9.9 -pudb==2022.1.3 +psutil==6.1.1 +psycopg==3.2.3 +psycopg2==2.9.10 +pudb==2024.1.3 py==1.11.0 -pycodestyle==2.11.1 -pycryptodome==3.20.0 -pydantic==2.1.1 -pydantic_core==2.4.0 +pycodestyle==2.12.1 +pycryptodome==3.21.0 pyflakes==3.2.0 -Pygments==2.15.1 -pyproject_hooks==1.0.0 +Pygments==2.18.0 +pyproject_hooks==1.2.0 pyrsistent==0.20.0 -pytest==7.4.0 -pytest-cov==4.1.0 -pytest-postgresql==5.1.0 +pyserial==3.5 +pytest==8.3.4 +pytest-cov==6.0.0 +pytest-postgresql==6.1.1 pytest-pudb==0.7.0 -pytest-timeout==2.2.0 +pytest-timeout==2.3.1 +python-dateutil==2.9.0.post0 pytimeparse==1.1.8 -pytz==2023.3 -PyYAML==6.0.1 +PyYAML==6.0.2 recommonmark==0.7.1 -redis==5.0.1 -referencing==0.29.3 -requests==2.31.0 +redis==5.2.1 +referencing==0.35.1 +requests==2.32.3 retry==0.9.2 rfc3339-validator==0.1.4 -rpds-py==0.9.2 -setuptools==68.0.0 -six==1.16.0 -smbus2==0.4.3 +rfc3986==1.5.0 +rpds-py==0.22.3 +setuptools==75.6.0 +six==1.17.0 +smbus2==0.5.0 +sniffio==1.3.1 snowballstemmer==2.2.0 sortedcontainers==2.4.0 -soupsieve==2.4.1 -Sphinx==7.2.6 -sphinx_mdinclude==0.5.3 -sphinxcontrib-applehelp==1.0.4 -sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==2.0.1 +soupsieve==2.6 +Sphinx==8.1.3 +sphinx_mdinclude==0.6.2 +sphinxcontrib-applehelp==2.0.0 +sphinxcontrib-devhelp==2.0.0 +sphinxcontrib-htmlhelp==2.1.0 sphinxcontrib-httpdomain==1.8.1 sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-openapi==0.8.3 -sphinxcontrib-programoutput==0.17 -sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.10 -tempora==5.5.0 +sphinxcontrib-openapi==0.8.4 +sphinxcontrib-programoutput==0.18 +sphinxcontrib-qthelp==2.0.0 +sphinxcontrib-serializinghtml==2.0.0 +tempora==5.7.0 termcolor==2.3.0 toml==0.10.2 -tornado==6.3.2 -typing_extensions==4.7.1 +tornado==6.4.2 +typeguard==4.4.1 +typing_extensions==4.12.2 unify==0.5 untokenize==0.1.1 -urllib3==2.0.4 -urwid==2.1.2 -urwid-readline==0.13 -wcwidth==0.2.6 -websocket-client==1.6.1 -Werkzeug==3.0.1 -wheel==0.40.0 -yamllint==1.34.0 -yarl==1.9.2 -yaspin==3.0.1 +urllib3==2.3.0 +urwid==2.6.16 +urwid_readline==0.15.1 +wcwidth==0.2.13 +websocket-client==1.8.0 +Werkzeug==3.1.3 +wheel==0.45.1 +yamllint==1.35.1 +yarl==1.18.3 +yaspin==3.1.0 zc.lockfile==3.0.post1