diff --git a/klippy/extras/verify_heater.py b/klippy/extras/verify_heater.py
index 7ac4d920f..378a8b940 100644
--- a/klippy/extras/verify_heater.py
+++ b/klippy/extras/verify_heater.py
@@ -13,6 +13,8 @@ for the parameters that control this check.
 class HeaterCheck:
     def __init__(self, config):
         self.printer = config.get_printer()
+        self.printer.register_event_handler("klippy:shutdown",
+                                            self.handle_shutdown)
         self.heater_name = config.get_name().split()[1]
         self.heater = None
         self.hysteresis = config.getfloat('hysteresis', 5., minval=0.)
@@ -38,7 +40,8 @@ class HeaterCheck:
             reactor = self.printer.get_reactor()
             self.check_timer = reactor.register_timer(self.check_event,
                                                       reactor.NOW)
-        elif state == 'shutdown' and self.check_timer is not None:
+    def handle_shutdown(self):
+        if self.check_timer is not None:
             reactor = self.printer.get_reactor()
             reactor.update_timer(self.check_timer, reactor.NEVER)
     def check_event(self, eventtime):
diff --git a/klippy/extras/virtual_sdcard.py b/klippy/extras/virtual_sdcard.py
index 1a0fc2a8e..e99e7cf4d 100644
--- a/klippy/extras/virtual_sdcard.py
+++ b/klippy/extras/virtual_sdcard.py
@@ -8,6 +8,7 @@ import os, logging
 class VirtualSD:
     def __init__(self, config):
         printer = config.get_printer()
+        printer.register_event_handler("klippy:shutdown", self.handle_shutdown)
         # sdcard state
         sd = config.get('path')
         self.sdcard_dirname = os.path.normpath(os.path.expanduser(sd))
@@ -24,8 +25,8 @@ class VirtualSD:
             self.gcode.register_command(cmd, getattr(self, 'cmd_' + cmd))
         for cmd in ['M28', 'M29', 'M30']:
             self.gcode.register_command(cmd, self.cmd_error)
-    def printer_state(self, state):
-        if state == 'shutdown' and self.work_timer is not None:
+    def handle_shutdown(self):
+        if self.work_timer is not None:
             self.must_pause_work = True
             try:
                 readpos = max(self.file_position - 1024, 0)
diff --git a/klippy/gcode.py b/klippy/gcode.py
index c2abbf45a..057f845a2 100644
--- a/klippy/gcode.py
+++ b/klippy/gcode.py
@@ -16,6 +16,7 @@ class GCodeParser:
     def __init__(self, printer, fd):
         self.printer = printer
         self.fd = fd
+        printer.register_event_handler("klippy:shutdown", self.handle_shutdown)
         # Input handling
         self.reactor = printer.get_reactor()
         self.is_processing_data = False
@@ -112,17 +113,16 @@ class GCodeParser:
             'homing_ypos': self.homing_position[1],
             'homing_zpos': self.homing_position[2]
         }
-    def printer_state(self, state):
-        if state == 'shutdown':
-            if not self.is_printer_ready:
-                return
-            self.is_printer_ready = False
-            self.gcode_handlers = self.base_gcode_handlers
-            self.dump_debug()
-            if self.is_fileinput:
-                self.printer.request_exit('error_exit')
-            self._respond_state("Shutdown")
+    def handle_shutdown(self):
+        if not self.is_printer_ready:
             return
+        self.is_printer_ready = False
+        self.gcode_handlers = self.base_gcode_handlers
+        self.dump_debug()
+        if self.is_fileinput:
+            self.printer.request_exit('error_exit')
+        self._respond_state("Shutdown")
+    def printer_state(self, state):
         if state != 'ready':
             if state == 'disconnect':
                 self._respond_state("Disconnect")
diff --git a/klippy/klippy.py b/klippy/klippy.py
index d81b4dc43..0ac421aef 100644
--- a/klippy/klippy.py
+++ b/klippy/klippy.py
@@ -51,14 +51,14 @@ class Printer:
         self.bglogger = bglogger
         self.start_args = start_args
         self.reactor = reactor.Reactor()
-        gc = gcode.GCodeParser(self, input_fd)
-        self.objects = collections.OrderedDict({'gcode': gc})
         self.reactor.register_callback(self._connect)
         self.state_message = message_startup
         self.is_shutdown = False
         self.run_result = None
-        self.state_cb = [gc.printer_state]
         self.event_handlers = {}
+        gc = gcode.GCodeParser(self, input_fd)
+        self.objects = collections.OrderedDict({'gcode': gc})
+        self.state_cb = [gc.printer_state]
     def get_start_args(self):
         return self.start_args
     def get_reactor(self):
@@ -183,8 +183,11 @@ class Printer:
             return
         self.is_shutdown = True
         self._set_state("%s%s" % (msg, message_shutdown))
-        for cb in self.state_cb:
-            cb('shutdown')
+        for cb in self.event_handlers.get("klippy:shutdown", []):
+            try:
+                cb()
+            except:
+                logging.exception("Exception during shutdown handler")
     def invoke_async_shutdown(self, msg):
         self.reactor.register_async_callback(
             (lambda e: self.invoke_shutdown(msg)))
diff --git a/klippy/mcu.py b/klippy/mcu.py
index 9b96b2528..a8e823c7a 100644
--- a/klippy/mcu.py
+++ b/klippy/mcu.py
@@ -422,6 +422,7 @@ class MCU:
         self._name = config.get_name()
         if self._name.startswith('mcu '):
             self._name = self._name[4:]
+        self._printer.register_event_handler("klippy:shutdown", self._shutdown)
         # Serial port
         self._serialport = config.get('serial', '/dev/ttyS0')
         baud = 0
@@ -769,8 +770,6 @@ class MCU:
             self._connect()
         elif state == 'disconnect':
             self._disconnect()
-        elif state == 'shutdown':
-            self._shutdown()
     def __del__(self):
         self._disconnect()
 
diff --git a/klippy/toolhead.py b/klippy/toolhead.py
index 458d5c517..cae280c70 100644
--- a/klippy/toolhead.py
+++ b/klippy/toolhead.py
@@ -205,6 +205,8 @@ class ToolHead:
         self.mcu = self.all_mcus[0]
         self.move_queue = MoveQueue()
         self.commanded_pos = [0., 0., 0., 0.]
+        self.printer.register_event_handler("klippy:shutdown",
+                                            self._handle_shutdown)
         # Velocity and acceleration control
         self.max_velocity = config.getfloat('max_velocity', above=0.)
         self.max_accel = config.getfloat('max_accel', above=0.)
@@ -412,13 +414,9 @@ class ToolHead:
         return { 'status': status, 'print_time': print_time,
                  'estimated_print_time': estimated_print_time,
                  'printing_time': print_time - last_print_start_time }
-    def printer_state(self, state):
-        if state == 'shutdown':
-            try:
-                self.move_queue.reset()
-                self.reset_print_time()
-            except:
-                logging.exception("Exception in toolhead shutdown")
+    def _handle_shutdown(self):
+        self.move_queue.reset()
+        self.reset_print_time()
     def get_kinematics(self):
         return self.kin
     def get_max_velocity(self):