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 .
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.