aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2024-08-16 21:34:21 -0400
committerKevin O'Connor <kevin@koconnor.net>2024-09-30 12:23:24 -0400
commit3358295de89fc24a99905568bc44d52c79022c4c (patch)
treeeec5383efcf60d21f37cb1e0af76503fdc10066a
parentef75346861fdf94d952a4e887fb843d215b00631 (diff)
downloadkutter-3358295de89fc24a99905568bc44d52c79022c4c.tar.gz
kutter-3358295de89fc24a99905568bc44d52c79022c4c.tar.xz
kutter-3358295de89fc24a99905568bc44d52c79022c4c.zip
led: Generalize template evaluation so it is not dependent on LEDs
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--klippy/extras/dotstar.py9
-rw-r--r--klippy/extras/led.py127
-rw-r--r--klippy/extras/neopixel.py5
-rw-r--r--klippy/extras/pca9533.py5
-rw-r--r--klippy/extras/pca9632.py5
5 files changed, 76 insertions, 75 deletions
diff --git a/klippy/extras/dotstar.py b/klippy/extras/dotstar.py
index 4186534f..0262c13d 100644
--- a/klippy/extras/dotstar.py
+++ b/klippy/extras/dotstar.py
@@ -1,9 +1,9 @@
# Support for "dotstar" leds
#
-# Copyright (C) 2019-2022 Kevin O'Connor <kevin@koconnor.net>
+# Copyright (C) 2019-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
-from . import bus
+from . import bus, led
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@@ -22,9 +22,8 @@ class PrinterDotstar:
self.spi = bus.MCU_SPI(mcu, None, None, 0, 500000, sw_spi_pins)
# Initialize color data
self.chain_count = config.getint('chain_count', 1, minval=1)
- pled = printer.load_object(config, "led")
- self.led_helper = pled.setup_helper(config, self.update_leds,
- self.chain_count)
+ self.led_helper = led.LEDHelper(config, self.update_leds,
+ self.chain_count)
self.prev_data = None
# Register commands
printer.register_event_handler("klippy:connect", self.handle_connect)
diff --git a/klippy/extras/led.py b/klippy/extras/led.py
index a2758ab8..e30f2d40 100644
--- a/klippy/extras/led.py
+++ b/klippy/extras/led.py
@@ -1,6 +1,6 @@
# Support for PWM driven LEDs
#
-# Copyright (C) 2019-2022 Kevin O'Connor <kevin@koconnor.net>
+# Copyright (C) 2019-2024 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging, ast
@@ -22,14 +22,22 @@ class LEDHelper:
blue = config.getfloat('initial_BLUE', 0., minval=0., maxval=1.)
white = config.getfloat('initial_WHITE', 0., minval=0., maxval=1.)
self.led_state = [(red, green, blue, white)] * led_count
+ # Support setting an led template
+ self.template_eval = lookup_template_eval(config)
+ self.tcallbacks = [(lambda text, s=self, index=i:
+ s._template_update(index, text))
+ for i in range(led_count)]
# Register commands
name = config.get_name().split()[-1]
gcode = self.printer.lookup_object('gcode')
gcode.register_mux_command("SET_LED", "LED", name, self.cmd_SET_LED,
desc=self.cmd_SET_LED_help)
- def get_led_count(self):
- return self.led_count
- def set_color(self, index, color):
+ gcode.register_mux_command("SET_LED_TEMPLATE", "LED", name,
+ self.cmd_SET_LED_TEMPLATE,
+ desc=self.cmd_SET_LED_TEMPLATE_help)
+ def get_status(self, eventtime=None):
+ return {'color_data': self.led_state}
+ def _set_color(self, index, color):
if index is None:
new_led_state = [color] * self.led_count
if self.led_state == new_led_state:
@@ -41,7 +49,17 @@ class LEDHelper:
new_led_state[index - 1] = color
self.led_state = new_led_state
self.need_transmit = True
- def check_transmit(self, print_time):
+ def _template_update(self, index, text):
+ try:
+ parts = [max(0., min(1., float(f)))
+ for f in text.split(',', 4)]
+ except ValueError as e:
+ logging.exception("led template render error")
+ parts = []
+ if len(parts) < 4:
+ parts += [0.] * (4 - len(parts))
+ self._set_color(index, tuple(parts))
+ def _check_transmit(self, print_time=None):
if not self.need_transmit:
return
self.need_transmit = False
@@ -62,9 +80,9 @@ class LEDHelper:
color = (red, green, blue, white)
# Update and transmit data
def lookahead_bgfunc(print_time):
- self.set_color(index, color)
+ self._set_color(index, color)
if transmit:
- self.check_transmit(print_time)
+ self._check_transmit(print_time)
if sync:
#Sync LED Update with print time and send
toolhead = self.printer.lookup_object('toolhead')
@@ -72,43 +90,40 @@ class LEDHelper:
else:
#Send update now (so as not to wake toolhead and reset idle_timeout)
lookahead_bgfunc(None)
- def get_status(self, eventtime=None):
- return {'color_data': self.led_state}
+ cmd_SET_LED_TEMPLATE_help = "Assign a display_template to an LED"
+ def cmd_SET_LED_TEMPLATE(self, gcmd):
+ index = gcmd.get_int("INDEX", None, minval=1, maxval=self.led_count)
+ set_template = self.template_eval.set_template
+ if index is not None:
+ set_template(gcmd, self.tcallbacks[index-1], self._check_transmit)
+ else:
+ for i in range(self.led_count):
+ set_template(gcmd, self.tcallbacks[i], self._check_transmit)
-# Main LED tracking code
-class PrinterLED:
+# Main template evaluation code
+class PrinterTemplateEvaluator:
def __init__(self, config):
self.printer = config.get_printer()
- self.led_helpers = {}
self.active_templates = {}
self.render_timer = None
# Load templates
dtemplates = display.lookup_display_templates(config)
self.templates = dtemplates.get_display_templates()
- gcode_macro = self.printer.lookup_object("gcode_macro")
+ gcode_macro = self.printer.load_object(config, "gcode_macro")
self.create_template_context = gcode_macro.create_template_context
- # Register handlers
- gcode = self.printer.lookup_object('gcode')
- gcode.register_command("SET_LED_TEMPLATE", self.cmd_SET_LED_TEMPLATE,
- desc=self.cmd_SET_LED_TEMPLATE_help)
- def setup_helper(self, config, update_func, led_count=1):
- led_helper = LEDHelper(config, update_func, led_count)
- name = config.get_name().split()[-1]
- self.led_helpers[name] = led_helper
- return led_helper
def _activate_timer(self):
if self.render_timer is not None or not self.active_templates:
return
reactor = self.printer.get_reactor()
self.render_timer = reactor.register_timer(self._render, reactor.NOW)
- def _activate_template(self, led_helper, index, template, lparams):
- key = (led_helper, index)
+ def _activate_template(self, callback, template, lparams, flush_callback):
if template is not None:
uid = (template,) + tuple(sorted(lparams.items()))
- self.active_templates[key] = (uid, template, lparams)
+ self.active_templates[callback] = (
+ uid, template, lparams, flush_callback)
return
- if key in self.active_templates:
- del self.active_templates[key]
+ if callback in self.active_templates:
+ del self.active_templates[callback]
def _render(self, eventtime):
if not self.active_templates:
# Nothing to do - unregister timer
@@ -122,37 +137,27 @@ class PrinterLED:
return self.templates[name].render(context, **kwargs)
context['render'] = render
# Render all templates
- need_transmit = {}
+ flush_callbacks = {}
rendered = {}
template_info = self.active_templates.items()
- for (led_helper, index), (uid, template, lparams) in template_info:
- color = rendered.get(uid)
- if color is None:
+ for callback, (uid, template, lparams, flush_callback) in template_info:
+ text = rendered.get(uid)
+ if text is None:
try:
text = template.render(context, **lparams)
- parts = [max(0., min(1., float(f)))
- for f in text.split(',', 4)]
except Exception as e:
- logging.exception("led template render error")
- parts = []
- if len(parts) < 4:
- parts += [0.] * (4 - len(parts))
- rendered[uid] = color = tuple(parts)
- need_transmit[led_helper] = 1
- led_helper.set_color(index, color)
+ logging.exception("display template render error")
+ text = ""
+ rendered[uid] = text
+ if flush_callback is not None:
+ flush_callbacks[flush_callback] = 1
+ callback(text)
context.clear() # Remove circular references for better gc
- # Transmit pending changes
- for led_helper in need_transmit.keys():
- led_helper.check_transmit(None)
+ # Invoke optional flush callbacks
+ for flush_callback in flush_callbacks.keys():
+ flush_callback()
return eventtime + RENDER_TIME
- cmd_SET_LED_TEMPLATE_help = "Assign a display_template to an LED"
- def cmd_SET_LED_TEMPLATE(self, gcmd):
- led_name = gcmd.get("LED")
- led_helper = self.led_helpers.get(led_name)
- if led_helper is None:
- raise gcmd.error("Unknown LED '%s'" % (led_name,))
- led_count = led_helper.get_led_count()
- index = gcmd.get_int("INDEX", None, minval=1, maxval=led_count)
+ def set_template(self, gcmd, callback, flush_callback=None):
template = None
lparams = {}
tpl_name = gcmd.get("TEMPLATE")
@@ -172,13 +177,17 @@ class PrinterLED:
lparams[p] = ast.literal_eval(v)
except ValueError as e:
raise gcmd.error("Unable to parse '%s' as a literal" % (v,))
- if index is not None:
- self._activate_template(led_helper, index, template, lparams)
- else:
- for i in range(led_count):
- self._activate_template(led_helper, i+1, template, lparams)
+ self._activate_template(callback, template, lparams, flush_callback)
self._activate_timer()
+def lookup_template_eval(config):
+ printer = config.get_printer()
+ te = printer.lookup_object("template_evaluator", None)
+ if te is None:
+ te = PrinterTemplateEvaluator(config)
+ printer.add_object("template_evaluator", te)
+ return te
+
PIN_MIN_TIME = 0.100
MAX_SCHEDULE_TIME = 5.0
@@ -205,8 +214,7 @@ class PrinterPWMLED:
% (config.get_name(),))
self.last_print_time = 0.
# Initialize color data
- pled = printer.load_object(config, "led")
- self.led_helper = pled.setup_helper(config, self.update_leds, 1)
+ self.led_helper = LEDHelper(config, self.update_leds, 1)
self.prev_color = color = self.led_helper.get_status()['color_data'][0]
for idx, mcu_pin in self.pins:
mcu_pin.setup_start_value(color[idx], 0.)
@@ -225,8 +233,5 @@ class PrinterPWMLED:
def get_status(self, eventtime=None):
return self.led_helper.get_status(eventtime)
-def load_config(config):
- return PrinterLED(config)
-
def load_config_prefix(config):
return PrinterPWMLED(config)
diff --git a/klippy/extras/neopixel.py b/klippy/extras/neopixel.py
index b6daeb4d..e72b8a91 100644
--- a/klippy/extras/neopixel.py
+++ b/klippy/extras/neopixel.py
@@ -4,6 +4,7 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
+from . import led
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@@ -40,9 +41,7 @@ class PrinterNeoPixel:
if len(self.color_map) > MAX_MCU_SIZE:
raise config.error("neopixel chain too long")
# Initialize color data
- pled = printer.load_object(config, "led")
- self.led_helper = pled.setup_helper(config, self.update_leds,
- chain_count)
+ self.led_helper = led.LEDHelper(config, self.update_leds, chain_count)
self.color_data = bytearray(len(self.color_map))
self.update_color_data(self.led_helper.get_status()['color_data'])
self.old_color_data = bytearray([d ^ 1 for d in self.color_data])
diff --git a/klippy/extras/pca9533.py b/klippy/extras/pca9533.py
index f84f9a65..a94e1334 100644
--- a/klippy/extras/pca9533.py
+++ b/klippy/extras/pca9533.py
@@ -4,7 +4,7 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging
-from . import bus
+from . import bus, led
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@@ -16,8 +16,7 @@ class PCA9533:
def __init__(self, config):
self.printer = config.get_printer()
self.i2c = bus.MCU_I2C_from_config(config, default_addr=98)
- pled = self.printer.load_object(config, "led")
- self.led_helper = pled.setup_helper(config, self.update_leds, 1)
+ self.led_helper = led.LEDHelper(config, self.update_leds, 1)
self.i2c.i2c_write([PCA9533_PWM0, 85])
self.i2c.i2c_write([PCA9533_PWM1, 170])
self.update_leds(self.led_helper.get_status()['color_data'], None)
diff --git a/klippy/extras/pca9632.py b/klippy/extras/pca9632.py
index 00876dc1..8a3551cf 100644
--- a/klippy/extras/pca9632.py
+++ b/klippy/extras/pca9632.py
@@ -3,7 +3,7 @@
# Copyright (C) 2022 Ricardo Alcantara <ricardo@vulcanolabs.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
-from . import bus, mcp4018
+from . import bus, led, mcp4018
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
@@ -34,8 +34,7 @@ class PCA9632:
raise config.error("Invalid color_order '%s'" % (color_order,))
self.color_map = ["RGBW".index(c) for c in color_order]
self.prev_regs = {}
- pled = printer.load_object(config, "led")
- self.led_helper = pled.setup_helper(config, self.update_leds, 1)
+ self.led_helper = led.LEDHelper(config, self.update_leds, 1)
printer.register_event_handler("klippy:connect", self.handle_connect)
def reg_write(self, reg, val, minclock=0):
if self.prev_regs.get(reg) == val: