mirror of https://github.com/Desuuuu/klipper.git
led: Support automated LED updates based on display_template results
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
a6ab56c0a6
commit
1ab3ac39e4
|
@ -3552,12 +3552,18 @@ text:
|
||||||
### [display_template]
|
### [display_template]
|
||||||
|
|
||||||
Display data text "macros" (one may define any number of sections with
|
Display data text "macros" (one may define any number of sections with
|
||||||
a display_template prefix). This feature allows one to reduce
|
a display_template prefix). See the
|
||||||
repetitive definitions in display_data sections. One may use the
|
[command templates](Command_Templates.md) document for information on
|
||||||
builtin render() function in display_data sections to evaluate a
|
template evaluation.
|
||||||
template. For example, if one were to define `[display_template
|
|
||||||
my_template]` then one could use `{ render('my_template') }` in a
|
This feature allows one to reduce repetitive definitions in
|
||||||
display_data section.
|
display_data sections. One may use the builtin `render()` function in
|
||||||
|
display_data sections to evaluate a template. For example, if one were
|
||||||
|
to define `[display_template my_template]` then one could use `{
|
||||||
|
render('my_template') }` in a display_data section.
|
||||||
|
|
||||||
|
This feature can also be used for continuous LED updates using the
|
||||||
|
[SET_LED_TEMPLATE](G-Codes.md#set_led_template) command.
|
||||||
|
|
||||||
```
|
```
|
||||||
[display_template my_template_name]
|
[display_template my_template_name]
|
||||||
|
@ -3570,9 +3576,9 @@ display_data section.
|
||||||
# "param_speed = 75" might have a caller with
|
# "param_speed = 75" might have a caller with
|
||||||
# "render('my_template_name', param_speed=80)". Parameter names may
|
# "render('my_template_name', param_speed=80)". Parameter names may
|
||||||
# not use upper case characters.
|
# not use upper case characters.
|
||||||
#text:
|
text:
|
||||||
# The text to return when the render() function is called for this
|
# The text to return when the this template is rendered. This field
|
||||||
# template. This field is evaluated using command templates (see
|
# is evaluated using command templates (see
|
||||||
# docs/Command_Templates.md). This parameter must be provided.
|
# docs/Command_Templates.md). This parameter must be provided.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -714,6 +714,23 @@ timeout. If careful timing is not needed, the optional SYNC=0
|
||||||
parameter can be specified to apply the changes without resetting the
|
parameter can be specified to apply the changes without resetting the
|
||||||
idle timeout.
|
idle timeout.
|
||||||
|
|
||||||
|
#### SET_LED_TEMPLATE
|
||||||
|
`SET_LED_TEMPLATE LED=<led_name> TEMPLATE=<template_name>
|
||||||
|
[INDEX=<index>]`: Assign a
|
||||||
|
[display_template](Config_Reference.md#display_template) to a given
|
||||||
|
[LED](Config_Reference.md#leds). For example, if one defined a
|
||||||
|
`[display_template my_led_template]` config section then one could
|
||||||
|
assign `TEMPLATE=my_led_template` here. The display_template should
|
||||||
|
produce a comma separated string containing four floating point
|
||||||
|
numbers corresponding to red, green, blue, and white color settings.
|
||||||
|
The template will be continuously evaluated and the LED will be
|
||||||
|
automatically set to the resulting colors. If INDEX is not specified
|
||||||
|
then all chips in the LED's daisy-chain will be set to the template,
|
||||||
|
otherwise only the chip with the given index will be updated. If
|
||||||
|
TEMPLATE is an empty string then this command will clear any previous
|
||||||
|
template assigned to the LED (one can then use `SET_LED` commands to
|
||||||
|
manage the LED's color settings).
|
||||||
|
|
||||||
### [output_pin]
|
### [output_pin]
|
||||||
|
|
||||||
The following command is available when an
|
The following command is available when an
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import logging
|
import logging
|
||||||
|
from .display import display
|
||||||
|
|
||||||
|
# Time between each led template update
|
||||||
|
RENDER_TIME = 0.500
|
||||||
|
|
||||||
# Helper code for common LED initialization and control
|
# Helper code for common LED initialization and control
|
||||||
class LEDHelper:
|
class LEDHelper:
|
||||||
|
@ -66,8 +70,97 @@ class LEDHelper:
|
||||||
class PrinterLED:
|
class PrinterLED:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
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")
|
||||||
|
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, has_white=False):
|
def setup_helper(self, config, update_func, led_count=1, has_white=False):
|
||||||
return LEDHelper(config, update_func, led_count, has_white)
|
led_helper = LEDHelper(config, update_func, led_count, has_white)
|
||||||
|
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):
|
||||||
|
key = (led_helper, index)
|
||||||
|
if template is not None:
|
||||||
|
self.active_templates[key] = template
|
||||||
|
return
|
||||||
|
if key in self.active_templates:
|
||||||
|
del self.active_templates[key]
|
||||||
|
def _render(self, eventtime):
|
||||||
|
if not self.active_templates:
|
||||||
|
# Nothing to do - unregister timer
|
||||||
|
reactor = self.printer.get_reactor()
|
||||||
|
reactor.register_timer(self.render_timer)
|
||||||
|
self.render_timer = None
|
||||||
|
return reactor.NEVER
|
||||||
|
# Setup gcode_macro template context
|
||||||
|
context = self.create_template_context(eventtime)
|
||||||
|
def render(name, **kwargs):
|
||||||
|
return self.templates[name].render(context, **kwargs)
|
||||||
|
context['render'] = render
|
||||||
|
# Render all templates
|
||||||
|
need_transmit = {}
|
||||||
|
rendered = {}
|
||||||
|
for (led_helper, index), template in self.active_templates.items():
|
||||||
|
color = rendered.get(template)
|
||||||
|
if color is None:
|
||||||
|
try:
|
||||||
|
text = template.render(context)
|
||||||
|
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[template] = color = tuple(parts)
|
||||||
|
prev_color = led_helper.led_state[index-1]
|
||||||
|
if color != prev_color:
|
||||||
|
if led_helper not in need_transmit:
|
||||||
|
need_transmit[led_helper] = 1
|
||||||
|
led_helper.led_state = list(led_helper.led_state)
|
||||||
|
led_helper.led_state[index-1] = color
|
||||||
|
context.clear() # Remove circular references for better gc
|
||||||
|
# Transmit pending changes
|
||||||
|
for led_helper in need_transmit.keys():
|
||||||
|
try:
|
||||||
|
led_helper.update_func(led_helper.led_state, None)
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("led template transmit error")
|
||||||
|
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.led_count
|
||||||
|
index = gcmd.get_int("INDEX", None, minval=1, maxval=led_count)
|
||||||
|
template = None
|
||||||
|
tpl_name = gcmd.get("TEMPLATE")
|
||||||
|
if tpl_name:
|
||||||
|
template = self.templates.get(tpl_name)
|
||||||
|
if template is None:
|
||||||
|
raise gcmd.error("Unknown display_template '%s'" % (tpl_name,))
|
||||||
|
if index is not None:
|
||||||
|
self._activate_template(led_helper, index, template)
|
||||||
|
else:
|
||||||
|
for i in range(led_count):
|
||||||
|
self._activate_template(led_helper, i+1, template)
|
||||||
|
self._activate_timer()
|
||||||
|
|
||||||
PIN_MIN_TIME = 0.100
|
PIN_MIN_TIME = 0.100
|
||||||
MAX_SCHEDULE_TIME = 5.0
|
MAX_SCHEDULE_TIME = 5.0
|
||||||
|
|
Loading…
Reference in New Issue