diff --git a/config/example-extras.cfg b/config/example-extras.cfg
index 1a7835a1d..e418cd3ad 100644
--- a/config/example-extras.cfg
+++ b/config/example-extras.cfg
@@ -1454,11 +1454,11 @@
 #   the command invoking the macro.
 #variable_<name>:
 #   One may specify any number of options with a "variable_" prefix.
-#   The given variable name will then be available during macro
-#   expansion. For example, a config with "variable_my_name = zebra"
-#   might have a gcode config containing "M117 My name is {my_name}".
-#   Variables can be changed at run-time using the SET_GCODE_VARIABLE
-#   command.
+#   The given variable name will be assigned the given value (parsed
+#   as a Python literal) and will be available during macro expansion.
+#   For example, a config with "variable_fan_speed = 75" might have
+#   gcode commands containing "M106 S{ fan_speed * 255 }". Variables
+#   can be changed at run-time using the SET_GCODE_VARIABLE command.
 
 
 # Enable the "M118" and "RESPOND" extended commands.
diff --git a/docs/Config_Changes.md b/docs/Config_Changes.md
index dc6935fd5..abce75be8 100644
--- a/docs/Config_Changes.md
+++ b/docs/Config_Changes.md
@@ -6,6 +6,11 @@ All dates in this document are approximate.
 
 # Changes
 
+20190607: The "variable_X" parameters of gcode_macro (along with the
+VALUE parameter of SET_GCODE_VARIABLE) are now parsed as Python
+literals. If a value needs to be assigned a string then wrap the value
+in quotes so that it is evaluated as a string.
+
 20190606: The "samples", "samples_result", and "sample_retract_dist"
 config options have been moved to the "probe" config section. These
 options are no longer supported in the "delta_calibrate", "bed_tilt",
diff --git a/docs/G-Codes.md b/docs/G-Codes.md
index 03d35460a..5d06a4a12 100644
--- a/docs/G-Codes.md
+++ b/docs/G-Codes.md
@@ -182,7 +182,8 @@ The following command is available when a "gcode_macro" config section
 is enabled:
 - `SET_GCODE_VARIABLE MACRO=<macro_name> VARIABLE=<name>
   VALUE=<value>`: This command allows one to change the value of a
-  gcode_macro variable at run-time.
+  gcode_macro variable at run-time. The provided VALUE is parsed as a
+  Python literal.
 
 ## Custom Pin Commands
 
diff --git a/klippy/extras/gcode_macro.py b/klippy/extras/gcode_macro.py
index 977a763d8..1918b0b9f 100644
--- a/klippy/extras/gcode_macro.py
+++ b/klippy/extras/gcode_macro.py
@@ -3,7 +3,7 @@
 # Copyright (C) 2018-2019  Kevin O'Connor <kevin@koconnor.net>
 #
 # This file may be distributed under the terms of the GNU GPLv3 license.
-import traceback, logging
+import traceback, logging, ast
 import jinja2
 
 
@@ -100,9 +100,16 @@ class GCodeMacro:
         prefix = 'default_parameter_'
         self.kwparams = { o[len(prefix):].upper(): config.get(o)
                           for o in config.get_prefix_options(prefix) }
+        self.variables = {}
         prefix = 'variable_'
-        self.variables = { o[len(prefix):]: config.get(o)
-                           for o in config.get_prefix_options(prefix) }
+        for option in config.get_prefix_options(prefix):
+            try:
+                self.variables[option[len(prefix):]] = ast.literal_eval(
+                    config.get(option))
+            except ValueError as e:
+                raise config.error(
+                    "Option '%s' in section '%s' is not a valid literal" % (
+                        option, config.get_name()))
     def get_status(self, eventtime):
         return dict(self.variables)
     cmd_SET_GCODE_VARIABLE_help = "Set the value of a G-Code macro variable"
@@ -115,7 +122,12 @@ class GCodeMacro:
                 return
             raise self.gcode.error("Unknown gcode_macro variable '%s'" % (
                 variable,))
-        self.variables[variable] = value
+        try:
+            literal = ast.literal_eval(value)
+        except ValueError as e:
+            raise self.gcode.error("Unable to parse '%s' as a literal" % (
+                value,))
+        self.variables[variable] = literal
     cmd_desc = "G-Code macro"
     def cmd(self, params):
         if self.in_script:
diff --git a/test/klippy/macros.cfg b/test/klippy/macros.cfg
index 4886425f4..8124aa3a7 100644
--- a/test/klippy/macros.cfg
+++ b/test/klippy/macros.cfg
@@ -80,13 +80,13 @@ gcode:
   {% endif %}
 
 [gcode_macro TEST_variable]
-variable_t: 12
+variable_t: 12.0
 gcode:
   { printer.gcode.action_respond_info("TEST_variable") }
-  {% if t|float - 12.0 != printer.toolhead.position.y %}
+  {% if t - 12.0 != printer.toolhead.position.y %}
     M112
   {% endif %}
-  {% if printer["gcode_macro TEST_variable"].t|float - 12.0 != 0.0 %}
+  {% if printer["gcode_macro TEST_variable"].t - 12.0 != 0.0 %}
     M112
   {% endif %}
   SET_GCODE_VARIABLE MACRO=TEST_variable VARIABLE=t VALUE=17
@@ -95,7 +95,7 @@ gcode:
 [gcode_macro TEST_variable_part2]
 gcode:
   { printer.gcode.action_respond_info("TEST_variable_part2") }
-  {% if printer["gcode_macro TEST_variable"].t|float != 17.0 %}
+  {% if printer["gcode_macro TEST_variable"].t != 17.0 %}
     M112
   {% endif %}