Octoprint mqtt 3D printer progress

Hey,

I wanted to share my current effort of an oh3 widget to monitor my 3D printer.

Some screenshots first, code follows below:

When octoprint is running, the printer automatically gets power through a rule as well and the widget “grows” to show detailed progress:

When octoprint is offline, the widget is collapsed

Resources

Here is the widget code:

uid: widget_3dprinter
tags: []
props:
  parameters:
    - description: Printer name for the title
      label: Name
      name: name
      required: false
      type: TEXT
    - description: The common prefix of all the octoprint MQTT items
      label: Item-Prefix
      name: item
      required: true
      type: TEXT
    - context: item
      description: item to toggle ocotPrint power (e.g. Outlet)
      label: Power-item
      name: powerItem
      required: false
      type: TEXT
    - description: Command options for the power item (OFF=Power off,ON=Power on)
      label: Command options
      name: powerCommand
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Mar 24, 2021, 10:39:54 AM
component: f7-card
config:
  class:
    - no-padding
slots:
  default:
    - component: Label
      config:
        class:
          - margin
          - no-padding
        text: =props.name
        style:
          text-align: left
          height: auto
    - component: f7-row
      config:
        class:
          - margin
      slots:
        default:
          - component: f7-col
            slots:
              default:
                - component: oh-label-card
                  config:
                    noBorder: true
                    noShadow: true
                    outline: false
                    visible: true
                    action: options
                    actionItem: =props.powerItem
                    actionOptions: =props.powerCommand
                    actionFeedback: Command sent
                    icon: f7:power
                    iconColor: "=(items[props.item + '_Connected'].state == 'disconnected') ? 'red' : 'green'"
                    label: "=(items[props.item + '_Connected'].state == 'disconnected') ? 'octoPrint disconnected' : 'octoPrint: Connected, Printer: '+items[props.item + '_State'].state "
                    class:
                      - no-padding
    - component: f7-row
      config:
        visible: =(items[props.item + '_Connected'].state == 'connected')
        class:
          - margin
      slots:
        default:
          - component: f7-col
            slots:
              default:
                - component: oh-gauge-card
                  config:
                    id: progress
                    title: "='File: '+items[props.item + '_Filename'].state+' (~'+Math.floor(((Number(items[props.item + '_TimePrinted'].state)+Number(items[props.item + '_TimeLeft'].state))/60/60))+'h'+('00' + Math.floor(((Number(items[props.item + '_TimePrinted'].state)+Number(items[props.item + '_TimeLeft'].state)))/60)%60).slice(-2)+'m)'"
                    type: circle
                    item: =(props.item + '_Progress')
                    size: 350
                    bg-color: transparent
                    border-bg-color: grey
                    border-color: darkorange
                    borderWidth: 25
                    value-text: text
                    value-text-color: red
                    value-font-size: 20
                    value-font-weight: 500
                    label-text: = '~'+Math.floor(items[props.item + '_TimeLeft'].state/60/60)+'h'+('00' + Math.floor(items[props.item + '_TimeLeft'].state/60)%60).slice(-2)+'m left'
                    label-font-size: 18
                    label-font-weight: 400
                    noBorder: true
                    noShadow: true
                    outline: true
                    class:
                      - justify-content-left
          - component: f7-col
            config:
              style:
                text-align: right
            slots:
              default:
                - component: oh-label-card
                  config:
                    noBorder: true
                    noShadow: true
                    title: Last Event
                    outline: true
                    icon: f7:captions_bubble
                    item: =(props.item + '_Event')
                    class:
                      - no-padding
                - component: oh-list-card
                  config:
                    mediaList: true
                    noBorder: true
                    noShadow: true
                    title: Data
                    footer: "='Last progress update: ' + items[props.item + '_Timestampoflastprogressmessage'].state"
                  slots:
                    default:
                      - component: oh-label-item
                        config:
                          item: =(props.item + '_HotendTemperature')
                          title: ='Hotend (T='+Math.floor(items[props.item + '_HotendTargetTemperature'].state)+')'
                          icon: f7:sort_down
                          iconColor: "=(items[props.item + '_HotendTemperature'].state <35  ? 'blue' : 'red')"
                          action: analyzer
                          actionAnalyzerCoordSystem: time
                          actionAnalyzerItems: =[props.item + '_BedTemperature', props.item + '_HotendTemperature', props.item + '_BedTargetTemperature', props.item + '_HotendTargetTemperature']
                      - component: oh-label-item
                        config:
                          item: =(props.item + '_BedTemperature')
                          title: ='Bed (T='+Math.floor(items[props.item + '_BedTargetTemperature'].state)+')'
                          icon: f7:rectangle_filled
                          iconColor: "=(items[props.item + '_BedTemperature'].state <35  ? 'blue' : 'red')"
                          action: analyzer
                          actionAnalyzerCoordSystem: time
                          actionAnalyzerItems: =[props.item + '_BedTemperature', props.item + '_HotendTemperature', props.item + '_BedTargetTemperature', props.item + '_HotendTargetTemperature']
                      - component: oh-label-item
                        config:
                          item: =(props.item + '_ZHeight')
                          title: ='Z-Height'
                          icon: f7:alt
                          action: analyzer
                          actionAnalyzerCoordSystem: time
                          actionAnalyzerItems: =[props.item + '_ZHeight']
                      - component: oh-label-item
                        config:
                          item: =(props.item + '_FilamentLength')
                          title: ='Filament Length'
                          icon: f7:arrow_up_down_square
                      - component: oh-label-item
                        config:
                          item: =(props.item + '_FilamentVolumen')
                          title: ='Filament Volume'
                          icon: f7:cube

To make it work, you need to install the mqtt plugin in octoprint, make it connect to the mqtt that you use in openhab as well (= you need an mqtt server and a configured mqtt binding). Be sure to enable the detailed progress and all the events in the octoprint mqtt plugin settings.

In openhab then define a new generic mqtt thing. Here is my thing definition:

UID: mqtt:topic:34c540dc:d619b28e3a
label: anycubic
thingTypeUID: mqtt:topic
configuration: {}
bridgeUID: mqtt:broker:34c540dc
channels:
  - id: connected
    channelTypeUID: mqtt:string
    label: Connected
    description: null
    configuration:
      stateTopic: octoPrint/mqtt
  - id: state
    channelTypeUID: mqtt:string
    label: State
    description: null
    configuration:
      stateTopic: octoPrint/event/PrinterStateChanged
      transformationPattern: JSONPATH:$.state_string
  - id: event
    channelTypeUID: mqtt:string
    label: Event
    description: null
    configuration:
      stateTopic: octoPrint/event/+
      transformationPattern: JSONPATH:$._event
  - id: filename
    channelTypeUID: mqtt:string
    label: Filename
    description: null
    configuration:
      stateTopic: octoPrint/progress/printing
      transformationPattern: JSONPATH:$.printer_data.job.file.name
  - id: progress
    channelTypeUID: mqtt:number
    label: Progress
    description: null
    configuration:
      stateTopic: octoPrint/progress/printing
      transformationPattern: JSONPATH:$.printer_data.progress.completion
      unit: "%"
  - id: printTime
    channelTypeUID: mqtt:number
    label: Time Printed
    description: null
    configuration:
      stateTopic: octoPrint/progress/printing
      transformationPattern: JSONPATH:$.printer_data.progress.printTime
  - id: printTimeLeft
    channelTypeUID: mqtt:number
    label: Time Left
    description: null
    configuration:
      stateTopic: octoPrint/progress/printing
      transformationPattern: JSONPATH:$.printer_data.progress.printTimeLeft
  - id: temperature_bed_actual
    channelTypeUID: mqtt:number
    label: Bed Temperature
    description: null
    configuration:
      stateTopic: octoPrint/temperature/bed
      transformationPattern: JSONPATH:$.actual
  - id: temperature_bed_target
    channelTypeUID: mqtt:number
    label: Bed Target Temperature
    description: null
    configuration:
      stateTopic: octoPrint/temperature/bed
      transformationPattern: JSONPATH:$.target
  - id: temperature_hotend_actual
    channelTypeUID: mqtt:number
    label: Hotend Temperature
    description: null
    configuration:
      stateTopic: octoPrint/temperature/tool0
      transformationPattern: JSONPATH:$.actual
  - id: temperature_hotend_target
    channelTypeUID: mqtt:number
    label: Hotend Target Temperature
    description: null
    configuration:
      stateTopic: octoPrint/temperature/tool0
      transformationPattern: JSONPATH:$.target
  - id: filvolume
    channelTypeUID: mqtt:number
    label: Filament Volumen
    description: null
    configuration:
      stateTopic: octoPrint/progress/printing
      transformationPattern: JSONPATH:$.printer_data.job.filament.*.volume
  - id: fillength
    channelTypeUID: mqtt:number
    label: Filament Length
    description: null
    configuration:
      stateTopic: octoPrint/progress/printing
      transformationPattern: JSONPATH:$.printer_data.job.filament.*.length
  - id: zHeight
    channelTypeUID: mqtt:number
    label: Z-Height
    description: null
    configuration:
      stateTopic: octoPrint/event/ZChange
      transformationPattern: JSONPATH:$.new
  - id: lastProgressTimestamp
    channelTypeUID: mqtt:datetime
    label: Timestamp of last progress message
    description: ""
    configuration:
      stateTopic: octoPrint/progress/printing
      transformationPattern: JSONPATH:$._timestamp

Create and link new items to all the channels, using the same prefix. In my case “anycubic” so that the item names are like “anycubic_Progress” “anycubic_Connected” etc.

If you have a power item, (e.g. for a controllable plug for the printer and/or octoprint server), you can set this in the widget props as well. This way you can start the printer from the widget or turn it off after a print. To make it harder to accidentally tap the power switch, I require to use the “command prompt” that pops up at the bottom of the screen, for an extra “confirm” click.

The widget is designed to pop out in it’s own page (for example from a generic progress label card), it doesn’t flow very well on an overview page. I’m not good with CSS and still very confused which CSS is supposed to go where (as there seem to be like 3 possibilities for each component) … so if anybody has some improvements, please share them :slight_smile:.

I’d also love to create an “inline” toggle widget in the top right corner (right of “printing” in the screenshot), for an autoshut down script to activate (simple rule with a timer). But all my attempts ended in either the toggle being above it or, when using columns, having the same size as the 2nd column from the other row and thus being way too big.

9 Likes

Thank you for sharing this widget.
Not certain of why but some of the Channel definitions are not vailable with my OcotoPrint: Version 1.5.3, python 3.7.3

Using MQTT Explorer it looks like some of the items have been changed to different channel or chanel items.
eg. the following printer_data.progress.printTimeLeft channel does not exist on my octoPi install:

  • id: printTimeLeft
    channelTypeUID: mqtt:number
    label: Time Left
    description: null
    configuration:
    stateTopic: octoPrint/progress/printing
    transformationPattern: JSONPATH:$.printer_data.progress.printTimeLeft

Here is a screen snip of what MQTT Explorer shows as available.
image

Just thought I’d let you know, and if anyone else runs into this as well.
I will update my channels and items to reflect what is available on my system, using MQTT Explorer as a guide.

Thanks for your work!!

Craig

Hey,

I’m running octoprint 1.5.3 as well, with the most recent stable mqtt Plugin.

Have you checked, that you have the Progress messages enabled in the plugin settings?

By the way, if anybody is reading this, I was also trying to create a smaller widget for mobile use, but failed to find a possiblity for a nice graphical progress bar (didn’t like the dimmer/slider). Any ideas for that?

Have a look here:

It might be something trivial, but I’m unable to get the f7-progressbar to work with the widget, the same way as the OH components.

                - component: f7-progressbar
                  config:
                    progress: 100.0

Works.

                - component: f7-progressbar
                  config:
                    progress: =(items['anycubic_Progress'].state)

doesn’t. And neither does (which I expected but tried)

           - component: f7-progressbar
                  config:
                    progress: =anycubic_Progress

The first one works fine in the expression tester though:

grafik

Try

progress: =Number(items['anycubic_Progress'].state)
1 Like

Thank you. Now I feel stupid, I didn’t even think about typecasting, as I didn’t need it before.

Thanks for this info, I did not notice that option when I was checking my MQTT plugin.
Enabled now and I see all the extra messages!.

Craig

Great widget - works really well but I would like to shrink the size of the last event text - im sure its simple but anyone help?

 - component: oh-label-card
                  config:
                    noBorder: true
                    noShadow: true
                    title: Last Event
                    outline: true
                    icon: f7:captions_bubble
                    item: =(props.item + '_Event')
                    class:
                      - no-padding

nice widget man,thnx,just a question if anyone knows,I use a relay connected with my Rpi and TogglePsu plugin to power on and off my printer,Is there a way to do it using the widget?

Yes, that’s what the power button in the upper left corner for. You can set item (power item) in the parameter to the switch item of your relay. And then add the command options in the next Parametee (e.g. OFF=Turn off,ON=Turn on)

i dont have a power item ,the relay is connected to octoprint’s raspberry io pins and i am using Togglepsu octoprint plugin to control printer’s power from octoprint ui.

Ah I have seen that plug-in, but don’t use it since my relay is an outlet that’s in openhab anyway.

Many octoprint plug-ins offer some info over mqtt as well, maybe look into this as a way to control it with open hab

1 Like

I found this

It should allow you to connect the psu relay to mqtt. Then you can create a generic mqtt thing in openhab and add a switch item to the relevant channel

1 Like

Very interesting…
Thank you for sharing!
Is the first time that i use a widget…
A question that probably is stupid… a widget can be used in a sitemap?

Not a stupid question, but the answer is no. Widgets are only usable in Main UI, not in Basic UI (sitemaps).

1 Like

Everything works for me, great job. Thank you. The only problem is that I can’t display the widget correctly in Paper UI