PrusaLink Monitor

Image preview of PrusaLink Monitor plugin.

prusa_link.30s.rb

Edit
Open on GitHub
#!/usr/bin/env ruby

# <xbar.title>PrusaLink Monitor</xbar.title>
# <xbar.version>v1.0</xbar.version>
# <xbar.author>Sebastian Ludwig</xbar.author>
# <xbar.author.github>sebastianludwig</xbar.author.github>
# <xbar.desc>Show the status of a Prusa 3D printer</xbar.desc>
# <xbar.dependencies>ruby</xbar.dependencies>
# <xbar.image>https://user-images.githubusercontent.com/1715010/257052937-7edd2773-79d0-4fc7-ba0f-84ddb8d4fb7d.png</xbar.image>

# <xbar.var>string(PRINTER_ADDRESS=""): Address of the PrusaLink web interface</xbar.var>
# <xbar.var>string(API_KEY=""): API key of your printer</xbar.var>

require "uri"
require "net/http"
require "json"
require "shellwords"

API_KEY = ENV["API_KEY"]
BASE_URI = ENV["PRINTER_ADDRESS"]

def json_api_request(path)
  uri = URI.join BASE_URI, path    
  Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
    request = Net::HTTP::Get.new uri.request_uri
    request["x-api-key"] = API_KEY

    response = http.request(request)
    break nil if not response.is_a?(Net::HTTPSuccess)

    begin
      json = JSON.parse!(response.body)
    rescue
      break nil
    end

    json
  end
end

def start_submenu
  puts "---"
  puts "Copy API key | shell=/bin/sh | param1='-c' | param2='echo #{API_KEY} | pbcopy'"
  puts "Refresh | refresh=true"
end

begin
  printer_json = json_api_request "api/printer"

  temp_nozzle = printer_json.dig("telemetry", "temp-nozzle")
  temp_bed = printer_json.dig("telemetry", "temp-bed")

  job_status_json = json_api_request "api/job"

  print_time_left_seconds = job_status_json.dig("progress", "printTimeLeft")
  completion = job_status_json.dig("progress", "completion")

  menu_bar_content = "%.0f/%.0f°C" % [temp_nozzle, temp_bed]
  menu_bar_content += " %3d%%" % (completion * 100) if completion
  menu_bar_content += "/-%dm" % (print_time_left_seconds / 60) if print_time_left_seconds

  puts menu_bar_content

  # Context menu

  temp_nozzle_target = printer_json.dig("temperature", "tool0", "target")
  temp_bed_target = printer_json.dig("temperature", "bed", "target")
  print_time_seconds = job_status_json.dig("progress", "printTime")

  start_submenu

  puts "State                 %s" % printer_json.dig("state", "text")
  puts "Nozzle               %3.0f °C" % temp_nozzle
  puts "Nozzle               %3.0f °C / %3.0f °C | alternate=true" % [temp_nozzle, temp_nozzle_target]
  puts "Bed                     %3.0f °C" % temp_bed
  puts "Bed                     %3.0f °C / %3.0f °C | alternate=true" % [temp_bed, temp_bed_target]
  puts "Material              %s" % printer_json.dig("telemetry", "material")

  puts "Print speed        %3d %%" % printer_json.dig("telemetry", "print-speed")

  puts "Progress              %3d %%" % (completion * 100) if completion
  puts "Z-height              %3.1f mm" % printer_json.dig("telemetry", "z-height")
  puts "Print time             %3d m" % (print_time_seconds / 60) if print_time_seconds
  puts "Print time left      %3d m" % (print_time_left_seconds / 60) if print_time_left_seconds
rescue => e
  puts "🤷‍♂️"
  
  start_submenu

  puts "Error: #{e} | shell=/bin/sh | param1='-c' | param2='echo #{Shellwords.escape(e)} | pbcopy'"
end