diff --git a/klippy/gcode.py b/klippy/gcode.py index 91e78ffa..9bacfbfa 100644 --- a/klippy/gcode.py +++ b/klippy/gcode.py @@ -78,16 +78,6 @@ class GCodeParser: self._handle_disconnect) printer.register_event_handler("extruder:activate_extruder", self._handle_activate_extruder) - # Register webhooks - webhooks = self.printer.lookup_object('webhooks') - webhooks.register_endpoint( - "gcode/help", self._handle_remote_help) - webhooks.register_endpoint( - "gcode/script", self._handle_remote_script) - webhooks.register_endpoint( - "gcode/restart", self._handle_remote_restart) - webhooks.register_endpoint( - "gcode/firmware_restart", self._handle_remote_firmware_restart) # Command handling self.is_printer_ready = False self.mutex = printer.get_reactor().mutex() @@ -158,6 +148,8 @@ class GCodeParser: "mux command %s %s %s already registered (%s)" % ( cmd, key, value, prev_values)) prev_values[value] = func + def get_command_help(self): + return dict(self.gcode_help) def register_output_handler(self, cb): self.output_callbacks.append(cb) def set_move_transform(self, transform, force=False): @@ -617,15 +609,6 @@ class GCodeParser: if cmd in self.gcode_help: cmdhelp.append("%-10s: %s" % (cmd, self.gcode_help[cmd])) gcmd.respond_info("\n".join(cmdhelp), log=False) - # Webhooks - def _handle_remote_help(self, web_request): - web_request.send(dict(self.gcode_help)) - def _handle_remote_restart(self, web_request): - self.run_script('restart') - def _handle_remote_firmware_restart(self, web_request): - self.run_script('firmware_restart') - def _handle_remote_script(self, web_request): - self.run_script(web_request.get('script')) # Support reading gcode from a pseudo-tty interface class GCodeIO: diff --git a/klippy/klippy.py b/klippy/klippy.py index 33a96a7a..838a43c0 100644 --- a/klippy/klippy.py +++ b/klippy/klippy.py @@ -59,7 +59,7 @@ class Printer: self.event_handlers = {} self.objects = collections.OrderedDict() # Init printer components that must be setup prior to config - for m in [webhooks, gcode]: + for m in [gcode, webhooks]: m.add_early_printer_objects(self) def get_start_args(self): return self.start_args diff --git a/klippy/webhooks.py b/klippy/webhooks.py index e923e986..d10f5c2c 100644 --- a/klippy/webhooks.py +++ b/klippy/webhooks.py @@ -45,12 +45,16 @@ class Sentinel: class WebRequest: error = WebRequestError - def __init__(self, base_request): + def __init__(self, client_conn, base_request): + self.client_conn = client_conn self.id = base_request['id'] self.path = base_request['path'] self.args = base_request['args'] self.response = None + def get_client_connection(self): + return self.client_conn + def get(self, item, default=Sentinel): if item not in self.args: if default == Sentinel: @@ -170,6 +174,9 @@ class ClientConnection: pass self.server.pop_client(self.uid) + def is_closed(self): + return self.fd_handle is None + def process_received(self, eventtime): try: data = self.sock.recv(4096) @@ -191,7 +198,7 @@ class ClientConnection: logging.debug( "webhooks: Request received: %s" % (req)) try: - web_request = WebRequest(json_loads_byteified(req)) + web_request = WebRequest(self, json_loads_byteified(req)) except Exception: logging.exception( "webhooks: Error decoding Server Request %s" @@ -256,22 +263,12 @@ class WebHooks: self.sconn = ServerSocket(self, printer) # Register Events - printer.register_event_handler( - "klippy:connect", self._handle_connect) printer.register_event_handler( "klippy:shutdown", self._notify_shutdown) - def _handle_connect(self): - gcode = self.printer.lookup_object('gcode') - gcode.register_output_handler(self._process_gcode_response) - def _notify_shutdown(self): self.call_remote_method("set_klippy_shutdown") - def _process_gcode_response(self, gc_response): - self.call_remote_method( - "process_gcode_response", response=gc_response) - def register_endpoint(self, path, callback): if path in self._endpoints: raise WebRequestError("Path already registered to an endpoint") @@ -318,6 +315,46 @@ class WebHooks: "action_call_remote_method": self._action_call_remote_method } +class GCodeHelper: + def __init__(self, printer): + self.printer = printer + self.gcode = printer.lookup_object("gcode") + # Output subscription tracking + self.is_output_registered = False + self.clients = {} + # Register webhooks + wh = printer.lookup_object('webhooks') + wh.register_endpoint("gcode/help", self._handle_help) + wh.register_endpoint("gcode/script", self._handle_script) + wh.register_endpoint("gcode/restart", self._handle_restart) + wh.register_endpoint("gcode/firmware_restart", + self._handle_firmware_restart) + wh.register_endpoint("gcode/subscribe_output", + self._handle_subscribe_output) + def _handle_help(self, web_request): + web_request.send(self.gcode.get_command_help()) + def _handle_script(self, web_request): + self.gcode.run_script(web_request.get('script')) + def _handle_restart(self, web_request): + self.gcode.run_script('restart') + def _handle_firmware_restart(self, web_request): + self.gcode.run_script('firmware_restart') + def _output_callback(self, msg): + for cconn, template in list(self.clients.items()): + if cconn.is_closed(): + del self.clients[cconn] + continue + tmp = dict(template) + tmp['params'] = {'response': msg} + cconn.send(tmp) + def _handle_subscribe_output(self, web_request): + cconn = web_request.get_client_connection() + template = web_request.get('response_template', {}) + self.clients[cconn] = template + if not self.is_output_registered: + self.gcode.register_output_handler(self._output_callback) + self.is_output_registered = True + SUBSCRIPTION_REFRESH_TIME = .25 class StatusHandler: @@ -443,4 +480,5 @@ class StatusHandler: def add_early_printer_objects(printer): printer.add_object('webhooks', WebHooks(printer)) + GCodeHelper(printer) StatusHandler(printer)