PrusaLink Web

Hi all!

This summer I decided to skip OctoPrint and use PrusaLink / Prusa Connect instead.

My reason for going from OctoPrint to PrusaLink / Prusa Connect is mainly because I want to keep my setup simple and reliable. The integration with PrusaLink / Prusa Connect, PrusaSlicer and https://www.printables.com/ works so nice. Here is a brief comparison:

OctoPrint pros

  • Many features and plugins (firmware update, bed leveling)
  • Supports streaming USB camera

OctoPrint cons

  • Needs constant tinkering
  • Needs total reinstall when OctoPi upgrades
  • Old and ugly gui
  • Missing some sensors in Home Assistant
  • No auto reconnect when powering off printer

PrusaLink / PrusaConnect pros

  • Minimal maintenance and configuration. Just install and use
  • Made by Prusa. Works well with PrusaSlicer, Prusa-printer and printables.com
  • Nice gui and features

PrusaLink / Prusa Connect cons

  • No plugins like OctoPrint
  • No integration for Home Assistant

I followed this guide for installing PrusaLink Web 0.7.0 on a Raspberry Pi3 with USB connection to my printer. Then I installed the PrusaLink integration in Home Assistant and found out this:

image

After some digging I found out that the integration PrusaLink only works for embedded PrusaLink in newer 32-bit “Buddy” printers. Not with PrusaLink Web installed on a Pi.
https://community.home-assistant.io/t/prusa-3d-printers-support/550070
https://github.com/home-assistant/core/issues/95529#issuecomment-1666508663

I don’t have the time to learn how to make PrusaLink integration work for my printer. So I spent some time making my own sensors, entities card and automation and want to share it with you.

When digging in the code for integration PrusaLink I found out that legacy API was used, not the new API v1 that is preferred by Prusa. There is mainly two endpoints using curl:

curl ‘http://prusalink.local/api/v1/status’ --digest -u username:password| json_pp
curl ‘http://prusalink.local/api/v1/job’ --digest -u username:password| json_pp

API-key is still supported but HTTP Digest Access Authentication is preferred as secrets is encrypted.

This was my idea from the beginning without knowing if it’s possible or not:

Sensors:

  • Printer Status
  • Nozzle Temperature
  • Nozzle Target Temperature
  • Heatbed Temperature
  • Heatbed Target Temperature
  • Print Speed
  • Z Height
  • Fan Hotend Speed
  • Fan Print Speed
  • Filename
  • Filament
  • Progress
  • Estimated Print Time
  • Time Printing
  • Time Remaining

Command buttons:

  • Cancel
  • Pause
  • Resume
  • Home
  • Z Up

Automations:

  • Send notification when print is done
  • Send notification some time before print is done
  • Send notification when error occurs

Camera/Picture:

  • View snapshots from camera
  • View thumbnail from gcode
  • View live camera feed from camera connected to PrusaLink

This is my solution so far. Entities card first:

entities:
  - entity: sensor.3d_printer_status
    name: Printer Status
  - entity: sensor.3d_printer_nozzle_temperature
    name: Nozzle Temperature
  - entity: sensor.3d_printer_nozzle_target_temperature
    name: Nozzle Target Temperature
  - entity: sensor.3d_printer_heatbed_temperature
    name: Heatbed Temperature
  - entity: sensor.3d_printer_heatbed_target_temperature
    name: Heatbed Target Temperature
  - entity: sensor.3d_printer_print_speed
    name: Print Speed
  - entity: sensor.3d_printer_z_height
    name: Z Height
  - entity: sensor.3d_printer_fan_hotend_speed
    name: Fan Hotend Speed
  - entity: sensor.3d_printer_fan_print_speed
    name: Fan Print Speed
  - type: divider
  - entity: sensor.3d_printer_filename
    name: Filename
  - entity: sensor.3d_printer_filament
    name: Filament
  - entity: sensor.3d_printer_progress
    name: Progress
  - entity: sensor.3d_printer_estimated_print_time
    name: Estimated Print Time
  - entity: sensor.3d_printer_time_printing
    name: Time Printing
  - entity: sensor.3d_printer_time_remaining
    name: Time Remaining
title: Prusa i3 MK3S+
state_color: true

And the RESTful sensors in configuration.yaml

rest:
  - resource: http://prusalink.local/api/v1/status
    authentication: digest
    username: !secret prusalink_username
    password: !secret prusalink_password
    scan_interval: 30

    sensor:
      - name: "3D-Printer Status"
        value_template: "{{ value_json.printer.state }}"
        icon: "mdi:printer-3d"

      - name: "3D-Printer Nozzle Temperature"
        value_template: "{{ value_json.printer.temp_nozzle }}"
        device_class: temperature
        unit_of_measurement: "°C"
        icon: "mdi:printer-3d-nozzle-heat"

      - name: "3D-Printer Nozzle Target Temperature"
        value_template: "{{ value_json.printer.target_nozzle }}"
        device_class: temperature
        unit_of_measurement: "°C"
        icon: "mdi:printer-3d-nozzle-heat"

      - name: "3D-Printer Heatbed Temperature"
        value_template: "{{ value_json.printer.temp_bed }}"
        device_class: temperature
        unit_of_measurement: "°C"
        icon: "mdi:radiator"

      - name: "3D-Printer Heatbed Target Temperature"
        value_template: "{{ value_json.printer.target_bed }}"
        device_class: temperature
        unit_of_measurement: "°C"
        icon: "mdi:radiator"

      - name: "3D-Printer Z Height"
        value_template: "{{ value_json.printer.axis_z }}"
        device_class: distance
        unit_of_measurement: "mm"
        icon: "mdi:arrow-up-down"

      - name: "3D-Printer Print Speed"
        value_template: "{{ value_json.printer.speed }}"
        unit_of_measurement: "%"
        icon: "mdi:speedometer"

      - name: "3D-Printer Fan Hotend Speed"
        value_template: "{{ value_json.printer.fan_hotend }}"
        unit_of_measurement: "rpm"
        icon: "mdi:fan"

      - name: "3D-Printer Fan Print Speed"
        value_template: "{{ value_json.printer.fan_print }}"
        unit_of_measurement: "rpm"
        icon: "mdi:fan"

  - resource: http://prusalink.local/api/v1/job
    authentication: digest
    username: !secret prusalink_username
    password: !secret prusalink_password
    scan_interval: 10

    sensor:
      - name: "3D-Printer Filename"
        value_template: "{{ value_json.file.name if value_json.state=='PRINTING' else None }}"
        icon: "mdi:file-image-outline"

      - name: "3D-Printer Filament"
        value_template: "{{ value_json.file.meta.filament_type if value_json.state=='PRINTING' else None }}"
        icon: "mdi:palette-swatch-variant"

      - name: "3D-Printer Progress"
        value_template: "{{ value_json.progress | round (0) }}"
        unit_of_measurement: "%"
        icon: "mdi:timer-sand"

      - name: "3D-Printer Estimated Print Time"
        value_template: " {{ timedelta(seconds=value_json.file.meta.estimated_print_time) if value_json.state=='PRINTING' else None }}"
        icon: "mdi:timer-sand-complete"

      - name: "3D-Printer Time Printing"
        value_template: "{{ timedelta(seconds=value_json.time_printing) if value_json.state=='PRINTING' else None }}"
        icon: "mdi:clock-start"

      - name: "3D-Printer Time Remaining"
        value_template: "{{ timedelta(seconds=value_json.time_remaining) if value_json.state=='PRINTING' else None }}"
        icon: "mdi:clock-end"

Command buttons in horizontal stack card:

image

type: horizontal-stack
cards:
  - show_name: true
    show_icon: true
    type: button
    tap_action:
      action: call-service
      service: shell_command.cancel
      target: {}
    name: Cancel
    icon: mdi:stop
    show_state: false
  - show_name: true
    show_icon: true
    type: button
    tap_action:
      action: call-service
      service: shell_command.pause
      target: {}
    name: Pause
    icon: mdi:pause
  - show_name: true
    show_icon: true
    type: button
    tap_action:
      action: call-service
      service: shell_command.resume
      target: {}
    name: Resume
    icon: mdi:play

Shell Command i configuration.yaml:

shell_command:
  cancel: "curl -X POST -H 'Content-Type: application/json' 'http://prusalink.local/api/job' --digest -u username:password -d '{\"command\": \"cancel\"}'"
  pause:  "curl -X POST -H 'Content-Type: application/json' 'http://prusalink.local/api/job' --digest -u username:password -d '{\"command\": \"pause\", \"action\": [\"pause\" ]}'"
  resume: "curl -X POST -H 'Content-Type: application/json' 'http://prusalink.local/api/job' --digest -u username:password -d '{\"command\": \"pause\", \"action\": [\"resume\"]}'"

Automation:

alias: Notify when 3d printer is done printing
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.3d_printer_status
    from: PRINTING
    to: IDLE
condition: []
action:
  - service: tts.speak
    data:
      cache: true
      media_player_entity_id: media_player.nest_mini
      message: Print is ready
      language: en
    target:
      entity_id: tts.google_en
mode: single

My comments so far:

  • I am happy with the sensors and entity card. It looks good and work for me.

  • Values for time can handle prints more than 24 hours using timedelta. Nice!

  • Only shows values for jobs when status is printing.

  • When Pi is constantly on and printer powers off when not in use, reconnecting to PrusaLink works

  • Password for rest uses secrets.yaml. Good!

  • Password for shell_commands do NOT use secrets.yaml. This is not good.

  • RESTful Command can not handle HTTP Digest Authentication, so I’m stuck with Shell Commands for now. Seems easy to implement but someone have to do it :slight_smile:

  • Command buttons have not ben tested fully yet. Don’t know if it’s working

  • There is a URL for a small gcode thumbnail in /api/v1/cameras/snap key file.refs.thumbnail. Have no idea how to store a picture in a variable and display on a picture card

  • There is a command for getting camera snapshot using: http://prusalink.local/api/v1/cameras/snap’ --digest -u username:password -o snapshot.png. But don’t know how to implement it.

  • All automations is not ready. To get a notification one minute before print is ready I was thinking about this: {{ as_timedelta(states(‘sensor.3d_printer_estimated_print_time’)) < timedelta(seconds=60) }} Trigger can monitor this for true

Enjoy and feel free to try out and comment.

Tomas Berglund Sweden

2 Likes

good morning,

I have the same problem as you with my printer so thank you very much for your script.
I just have a question, where do you add the login and password?
Thanks in advance for your help.

A PR to resolve this issue was put together and then rejected because the HA team wants that to happen in the underlying library. Another fix was put forward using the method suggested by the first rejected fix and that has been stuck in approval hell for nearly a year. Yet another fix has been suggested, maybe in a year or two someone might decide that a working solution is better than a year plus of a broken solution but that hasn’t happened yet so who knows.