diff --git a/ks_includes/defaults.conf b/ks_includes/defaults.conf index 5f5294f1..7c8c44eb 100644 --- a/ks_includes/defaults.conf +++ b/ks_includes/defaults.conf @@ -110,6 +110,12 @@ icon: custom-script panel: gcode_macros enable: {{ printer.gcode_macros.count > 0 }} +[menu __main actions pins] +name: {{ gettext('Pins') }} +icon: fine-tune +panel: pins +enable: {{ printer.output_pins.count > 0 }} + [menu __main actions power] name: {{ gettext('Power') }} icon: shutdown diff --git a/ks_includes/printer.py b/ks_includes/printer.py index 89dda631..17a65c81 100644 --- a/ks_includes/printer.py +++ b/ks_includes/printer.py @@ -30,6 +30,7 @@ class Printer: self.extrudercount = 0 self.tempdevcount = 0 self.fancount = 0 + self.output_pin_count = 0 def reset(self): GLib.source_remove(self.store_timeout) @@ -47,6 +48,7 @@ class Printer: self.config = None self.klipper = None self.tempstore = None + self.output_pin_count = None def reinit(self, printer_info, data): logging.debug(f"Moonraker object status: {data}") @@ -54,6 +56,7 @@ class Printer: self.extrudercount = 0 self.tempdevcount = 0 self.fancount = 0 + self.output_pin_count = 0 self.tools = [] self.devices = {} self.data = data @@ -95,6 +98,10 @@ class Printer: # Support for hiding devices by name if not " ".join(x.split(" ")[1:]).startswith("_"): self.fancount += 1 + if x.startswith('output_pin '): + # Support for hiding devices by name + if not " ".join(x.split(" ")[1:]).startswith("_"): + self.output_pin_count += 1 if x.startswith('bed_mesh '): r = self.config[x] r['x_count'] = int(r['x_count']) @@ -110,6 +117,7 @@ class Printer: logging.info(f"# Extruders: {self.extrudercount}") logging.info(f"# Temperature devices: {self.tempdevcount}") logging.info(f"# Fans: {self.fancount}") + logging.info(f"# Output pins: {self.output_pin_count}") def process_update(self, data): for x in (self.get_tools() + self.get_heaters() + self.get_filament_sensors()): @@ -206,6 +214,12 @@ class Printer: fans.extend(iter(self.get_config_section_list(f"{fan_type} "))) return fans + def get_output_pins(self): + output_pins = [] + output_pins.extend(iter(self.get_config_section_list("output_pin "))) + logging.debug(f"{output_pins}") + return output_pins + def get_gcode_macros(self): return self.get_config_section_list("gcode_macro ") @@ -229,6 +243,7 @@ class Printer: "extruders": {"count": self.extrudercount}, "temperature_devices": {"count": self.tempdevcount}, "fans": {"count": self.fancount}, + "output_pins": {"count": self.output_pin_count}, "bltouch": self.config_section_exists("bltouch"), "gcode_macros": {"count": len(self.get_gcode_macros())}, "idle_timeout": self.get_stat("idle_timeout").copy(), @@ -298,6 +313,9 @@ class Printer: speed = 0 return speed + def get_pin_value(self, pin): + return self.data[pin]["value"] if pin in self.data else 0 + def get_extruder_count(self): return self.extrudercount diff --git a/panels/network.py b/panels/network.py index e84396a4..d30c88eb 100644 --- a/panels/network.py +++ b/panels/network.py @@ -30,7 +30,6 @@ class NetworkPanel(ScreenPanel): logging.info(f"Found wireless interfaces: {self.wireless_interfaces}") self.wifi = WifiManager(self.wireless_interfaces[0]) - def initialize(self, menu): grid = self._gtk.HomogeneousGrid() grid.set_hexpand(True) diff --git a/panels/pins.py b/panels/pins.py new file mode 100644 index 00000000..7b673a8e --- /dev/null +++ b/panels/pins.py @@ -0,0 +1,118 @@ +import gi +import logging + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GLib, Pango + +from ks_includes.screen_panel import ScreenPanel + + +def create_panel(*args): + return OutputPinPanel(*args) + + +class OutputPinPanel(ScreenPanel): + + def initialize(self, panel_name): + + self.devices = {} + # Create a grid for all devices + self.labels['devices'] = Gtk.Grid() + self.labels['devices'].set_valign(Gtk.Align.CENTER) + + self.load_pins() + + scroll = self._gtk.ScrolledWindow() + scroll.add(self.labels['devices']) + + self.content.add(scroll) + + def load_pins(self): + output_pins = self._printer.get_output_pins() + for pin in output_pins: + # Support for hiding devices by name + name = " ".join(pin.split(" ")[1:]) + if name.startswith("_"): + continue + self.add_pin(pin) + + frame = Gtk.Frame() + frame.set_vexpand(False) + self.labels['devices'].attach(frame, 0, -1, 1, 1) + + def add_pin(self, pin): + + logging.info(f"Adding pin: {pin}") + name = Gtk.Label() + name.set_markup(f'\n{" ".join(pin.split(" ")[1:])}\n') + name.set_hexpand(True) + name.set_vexpand(True) + name.set_halign(Gtk.Align.START) + name.set_valign(Gtk.Align.CENTER) + name.set_line_wrap(True) + name.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR) + + scale = Gtk.Scale.new_with_range(orientation=Gtk.Orientation.HORIZONTAL, min=0, max=100, step=1) + scale.set_value(self.check_pin_value(pin)) + scale.set_digits(0) + scale.set_hexpand(True) + scale.set_has_origin(True) + scale.get_style_context().add_class("fan_slider") + scale.connect("button-release-event", self.set_output_pin, pin) + + min_btn = self._gtk.ButtonImage("cancel", None, "color1", 1) + min_btn.set_hexpand(False) + min_btn.connect("clicked", self.update_pin_value, pin, 0) + + pin_col = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) + pin_col.add(min_btn) + pin_col.add(scale) + + pin_row = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + pin_row.add(name) + pin_row.add(pin_col) + + frame = Gtk.Frame() + frame.get_style_context().add_class("frame-item") + frame.add(pin_row) + + self.devices[pin] = { + "row": frame, + "scale": scale, + } + + devices = sorted(self.devices) + pos = devices.index(pin) + + self.labels['devices'].insert_row(pos) + self.labels['devices'].attach(self.devices[pin]['row'], 0, pos, 1, 1) + self.labels['devices'].show_all() + + def set_output_pin(self, widget, event, pin): + self._screen._ws.klippy.gcode_script(f'SET_PIN PIN={" ".join(pin.split(" ")[1:])} ' + f'VALUE={self.devices[pin]["scale"].get_value() / 100}') + # Check the speed in case it wasn't applied + GLib.timeout_add_seconds(1, self.check_pin_value, pin) + + def check_pin_value(self, pin): + self.update_pin_value(None, pin, self._printer.get_pin_value(pin)) + return False + + def process_update(self, action, data): + if action != "notify_status_update": + return + + for pin in self.devices: + if pin in data and "value" in data[pin]: + self.update_pin_value(None, pin, data[pin]["value"]) + + def update_pin_value(self, widget, pin, value): + if pin not in self.devices: + return + + self.devices[pin]['scale'].disconnect_by_func(self.set_output_pin) + self.devices[pin]['scale'].set_value(round(float(value) * 100)) + self.devices[pin]['scale'].connect("button-release-event", self.set_output_pin, pin) + + if widget is not None: + self.set_output_pin(None, None, pin) diff --git a/screen.py b/screen.py index d93699ba..13442bd0 100644 --- a/screen.py +++ b/screen.py @@ -270,6 +270,8 @@ class KlipperScreen(Gtk.Window): requested_updates['objects'][f] = ["speed"] for f in self.printer.get_filament_sensors(): requested_updates['objects'][f] = ["enabled", "filament_detected"] + for p in self.printer.get_output_pins(): + requested_updates['objects'][p] = ["value"] self._ws.klippy.object_subscription(requested_updates)