Gcode STL preview in Home Assistant revisited + other Moonraker sensors

Since Octoprint is hardly the optimal UI in this day and age, I’ve changed to using Moonraker and interfaces based on that. This means some parts of my Home Assistant configuration had to change.

Update: Moonraker stores preview images now – a comment describes a cleaner way to achieve the same end result.

This is how I got the STL preview embedded in gcode from the Klipper websocket service Moonraker to Home Assistant. Everything assumes your Klipper and Moonraker already work as intended.

Here’s the whole printer info code, and another sensor that extracts all the metadata from print preview. Note this takes the full size image, as my slicer generates a smaller thumbnail too. Be sure the json attributes path matches your slicer – this should be good for slic3r variants such as SuperSlicer. Big thanks to Arksine for the example. Take note of the number of thumbnail sizes your slicer creates – this sensor takes the 2nd preview image generated.

#sensors.yaml

  - platform: rest
    name: spaghetti_sensor
    resource: "http://192.168.1.20:7125/printer/objects/query?heater_bed&extruder&print_stats&toolhead&display_status&virtual_sdcard"
    json_attributes_path: "$.result.status"
    json_attributes:
      - heater_bed
      - extruder
      - print_stats
      - toolhead
      - display_status
      - virtual_sdcard
    value_template: 'OK'
  - platform: template
    sensors:
      spaghetti_hotend_target:
        friendly_name: 'Hotend Target'
        value_template: '{{ states.sensor.spaghetti_sensor.attributes["extruder"]["target"]  | float | round(1) }}'
        device_class: temperature
        unit_of_measurement: '°C'
        icon_template: mdi:thermometer
      spaghetti_hotend_actual:
        friendly_name: 'Hotend Actual'
        value_template: '{{ states.sensor.spaghetti_sensor.attributes["extruder"]["temperature"]  | float | round(1) }}'
        device_class: temperature
        unit_of_measurement: '°C'
        icon_template: mdi:thermometer
      spaghetti_bed_target:
        friendly_name: 'Bed Target'
        value_template: '{{ states.sensor.spaghetti_sensor.attributes["heater_bed"]["target"]  | float | round(1) }}'
        device_class: temperature
        unit_of_measurement: '°C'
        icon_template: mdi:thermometer
      spaghetti_bed_actual:
        friendly_name: 'Bed Actual'
        value_template: '{{ states.sensor.spaghetti_sensor.attributes["heater_bed"]["temperature"]  | float | round(1) }}'
        device_class: temperature
        unit_of_measurement: '°C'
        icon_template: mdi:thermometer
      spaghetti_state:
        friendly_name: 'Printer State'
        value_template: '{{ states.sensor.spaghetti_sensor.attributes["print_stats"]["state"]}}'
        icon_template: >
          {% set val =  states.sensor.spaghetti_sensor.attributes["print_stats"]["state"]  %}
          {% if val == 'standby' %}
            mdi:sleep
          {% elif val == 'error' %}
            mdi:alert-circle
          {% elif val == 'printing' %}
            mdi:printer-3d-nozzle
          {% elif val == 'paused' %}
            mdi:pause-circle
          {% elif val == 'complete' %}
            mdi:printer-3d
          {% else %}
            mdi:help-circle
          {% endif %}
      spaghetti_current_print:
        friendly_name: 'Current Print'
        value_template: '{{ states.sensor.spaghetti_sensor.attributes["print_stats"]["filename"]}}'
      spaghetti_current_progress:
        friendly_name: 'Progress'
        value_template: '{{ ((states.sensor.spaghetti_sensor.attributes["display_status"]["progress"])*100) | round(0, "floor") }}'
        unit_of_measurement: '%'
        icon_template: mdi:progress-clock
      spaghetti_print_time:
        friendly_name: 'Time Elapsed'
        value_template: '{{ states.sensor.spaghetti_sensor.attributes["print_stats"]["print_duration"] |timestamp_custom("%H:%M:%S", 0)}}'
        icon_template: mdi:camera-timer
      spaghetti_time_remaining:
        friendly_name: 'Time Remaining'
        value_template: '{{ (((states.sensor.spaghetti_sensor.attributes["print_stats"]["print_duration"]/states.sensor.spaghetti_sensor.attributes["display_status"]["progress"]- states.sensor.spaghetti_sensor.attributes["print_stats"]["print_duration"]) if states.sensor.spaghetti_sensor.attributes["display_status"]["progress"]>0 else 0)) | timestamp_custom("%H:%M:%S", 0)}}'
        icon_template: mdi:timer-sand
      spaghetti_eta:
        friendly_name: 'ETA'
        value_template: '{{ (as_timestamp(now())+2*60*60+((states.sensor.spaghetti_sensor.attributes["print_stats"]["print_duration"]/states.sensor.spaghetti_sensor.attributes["display_status"]["progress"]- states.sensor.spaghetti_sensor.attributes["print_stats"]["print_duration"]) if states.sensor.spaghetti_sensor.attributes["display_status"]["progress"]>0 else 0)) | timestamp_custom("%H:%M:%S", 0)}}'
        icon_template: mdi:av-timer
      spaghetti_nozzletemp:
        friendly_name: 'Nozzle Temperature'
        value_template: '{{[( states.sensor.spaghetti_sensor.attributes["extruder"]["temperature"]  | float | round(1)| string)," / ",( states.sensor.spaghetti_sensor.attributes["extruder"]["target"]  | float | round(1)| string)]|join}}'
        icon_template: mdi:thermometer
      spaghetti_bedtemp:
        friendly_name: 'Bed Temperature'
        value_template: '{{[( states.sensor.spaghetti_sensor.attributes["heater_bed"]["temperature"]  | float | round(1)| string)," / ",( states.sensor.spaghetti_sensor.attributes["heater_bed"]["target"]  | float | round(1)| string)]|join}}'
        icon_template: mdi:thermometer

04/03/2021: Updated this camera entity to properly display the placeholder image

#cameras.yaml
- platform: generic
  name: klipper_gcode_preview
  still_image_url: >
    {% if states.sensor.spaghetti_state.state == 'standby' %} https://foo.bar.org:8123/local/placeholder.png
    {% elif states.sensor.spaghetti_state.state == 'complete' %} https://foo.bar.org:8123/local/placeholder.png
    {% elif states.sensor.spaghetti_state.state == 'unknown' %} https://foo.bar.org:8123/local/placeholder.png
    {% else %} http://192.168.1.20:7125/server/files/gcodes/.thumbs/{{ states.sensor.spaghetti_sensor.attributes['print_stats']['filename'][:-6]}}-250x250.png
    {% endif %}
  scan_interval: 5
  content_type: image/png
  verify_ssl: false
Bookmark the permalink.

5 Comments

  1. Thank you for this, it helped me a lot setting things up in HomeAssistant.

    It’s possible to load the gcode thumbnail directly from the Klipper/Moonraker host when your slicer provides the thumbnail, no need for the python script and automation.
    There might be a better way, I’m no expert, but this worked for me 🙂

    1. Remove ‘.gcode’ from filename
    In sensors.yaml for ‘current_print’ sensor:
    value_template: ‘{{ states.sensor.spaghetti_sensor.attributes[“print_stats”][“filename”][:-6]}}’

    2. Use filename to load gcode preview thumbnail directly from Klipper host
    In cameras.yaml for ‘klipper_gcode_preview’ camera:
    still_image_url: http://192.168.1.20/server/files/gcodes/.thumbs/{{ states((‘sensor.spaghetti_current_print’)) }}-400×300.png

    • Thanks for the comment. Moonraker indeed added that feature some months after this writeup, and what you describe is pretty much the current best practise.

  2. Anyone reading this and have problem (I had huge problem to implement that. This is my solution.

    1)
    in ((‘sensor.spaghetti_current_print’)) you need to change ‘’ to ”

    and the second one … much more tricky

    2) in 400×300.png you need to change x. I don’t know to what other letter (it look like x but it apparently is different letter) just go to your klipper right-click on stl preview and copy image address. Pase it to notepad and copy-paste x with x in 400×300.png

    It looks almost identical but it is different. Took me ages to solve this. Hope someone will found it helpfull.

  3. Hi there,
    Thank you for this i have finally found something that worked ,just wondering if the updated cleaner way of getting the stl preview has been added to your code now or do i have to change that still please.

    • Yes, the camera template could be neater but all of it technically works with an up to date klipper / moonraker installation, given that you adjust IPs and addresses to match your system.

Leave a Reply

Your email address will not be published. Required fields are marked *