Skip to content

Commit

Permalink
v1.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sco01 committed Sep 18, 2019
1 parent b028090 commit ebc211a
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 38 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ A dashboard tab for Octoprint that displays the most relevant info regarding the

![Screenshot](https://github.com/StefanCohen/OctoPrint-Dashboard/blob/master/screenshot-theme.png)

## What's new?
For release notes and release history, please visit the [wiki](https://github.com/StefanCohen/OctoPrint-Dashboard/wiki).

## Known limitations
* Translations to other languages are not supported yet.
* The CPU-temp will likely only work on a Raspberry Pi.
* Disk Usage will likely only work on Linux deratives.

## Dependencies

Expand All @@ -38,6 +42,7 @@ The dashboard uses the time estimates provided by PrintTimeGenius if it is insta

Inspired by OctoDash: https://github.com/UnchartedBull/OctoDash/
Icons from: http://www.iconninja.com
Github Contributors: Andy Harrison (wizard04wsu), Doug Hoyt (doughoyt)

## Setup

Expand All @@ -48,7 +53,7 @@ Install via the bundled [Plugin Manager](https://github.com/foosel/OctoPrint/wik
## Configuration

* Two progress gauge types can be configured in the plugin settings: Bar & Circle (default). The Bar gauge can be useful if you have multiple hotends or a heated chamber.
* Octoprint defaults to showing "fuzzy print time estimates" and these are displayed by the dashboard (example: Estimated print time: 1 hour). If you prefer the hh:mm:ss format used elsewhere, go to Settings/Appearance and untick "Show fuzzy print time estimates".
* For more configuration help, please visit the [wiki](https://github.com/StefanCohen/OctoPrint-Dashboard/wiki).

For users of Themeify:

Expand Down
32 changes: 22 additions & 10 deletions octoprint_dashboard/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
# coding=utf-8
from __future__ import absolute_import
import octoprint.plugin
from octoprint.util import RepeatedTimer
import psutil


class DashboardPlugin(octoprint.plugin.SettingsPlugin,
octoprint.plugin.StartupPlugin,
octoprint.plugin.AssetPlugin,
octoprint.plugin.TemplatePlugin,
octoprint.plugin.EventHandlerPlugin):

def psUtilGetStats(self):
thermal = psutil.sensors_temperatures(fahrenheit=False)
cpuTemp = round((thermal["cpu-thermal"][0][1]))
self._plugin_manager.send_plugin_message(self._identifier, dict(cpuPercent=str(psutil.cpu_percent(interval=None, percpu=False)),
virtualMemPercent=str(psutil.virtual_memory().percent),
diskUsagePercent=str(psutil.disk_usage("/").percent),
cpuTemp=str(cpuTemp)))


def on_after_startup(self):
self._logger.info("Dashboard started")
self._logger.info("Dashboard started")
self._logger.info("virtualMem: " + str(psutil.cpu_percent(interval=None, percpu=False)))
self.timer = RepeatedTimer(2.0, self.psUtilGetStats, run_first=True)
self.timer.start()

def on_event(self, event, payload):
if event == "DisplayLayerProgress_layerChanged" or event == "DisplayLayerProgress_progressChanged" or event == "DisplayLayerProgress_fanspeedChanged":
newCurrentLayer = 0
newTotalLayer = 0
#self._logger.info("Layer: " + payload.get('currentLayer'))
if payload.get('totalLayer').isdigit():
newTotalLayer = int(payload.get('totalLayer')) + 1 # Because DisplayLayerProgress is base 0
if payload.get('currentLayer').isdigit():
newCurrentLayer = int(payload.get('currentLayer')) + 1
self._plugin_manager.send_plugin_message(self._identifier, dict(totalLayer=newTotalLayer,
currentLayer=newCurrentLayer,
self._plugin_manager.send_plugin_message(self._identifier, dict(totalLayer=payload.get('totalLayer'),
currentLayer=payload.get('currentLayer'),
currentHeight=payload.get('currentHeight'),
totalHeightWithExtrusion=payload.get('totalHeightWithExtrusion'),
feedrate=payload.get('feedrate'),
Expand All @@ -37,7 +46,10 @@ def get_settings_defaults(self):
gaugetype="circle",
hotendTempMax="300",
bedTempMax="100",
chamberTempMax="50"
chamberTempMax="50",
showFan=True,
showWebCam=False,
showSystemInfo=False
)

def get_template_configs(self):
Expand Down
15 changes: 15 additions & 0 deletions octoprint_dashboard/static/css/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,18 @@ svg text {
font-family: sans-serif;
fill: #08c;
}

#webcamstreamer_wrapper {
position: relative;
padding-bottom: 56.25%;
width: 100%;
height: 100%;
}

#webcamstreamer_wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Binary file added octoprint_dashboard/static/img/cpu-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added octoprint_dashboard/static/img/hdd-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added octoprint_dashboard/static/img/ram-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 32 additions & 9 deletions octoprint_dashboard/static/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,27 @@ $(function() {
self.lastLayerDuration = ko.observable("-");
self.averageLayerDuration = ko.observable("-");
self.getEta = ko.observable();
self.embedUrl = ko.observable("");

self.cpuPercent = ko.observable(0);
self.virtualMemPercent = ko.observable(0);
self.diskUsagePercent = ko.observable(0);
self.cpuTemp = ko.observable(0);


//Notify user if displaylayerprogress plugin is not installed
self.DisplayLayerProgressAvailable = function() {
if (self.settingsViewModel.settings.plugins.DisplayLayerProgress)
return;
else return "Can't get stats from <a href='https://plugins.octoprint.org/plugins/DisplayLayerProgress/' target='_blank'>DisplayLayerprogress</a>. Is it installed, enabled and on the latest version?";
else {
printerDisplay = new PNotify({
title: 'Dashboard',
type: 'warning',
text: 'Can\'t get stats from <a href="https://plugins.octoprint.org/plugins/DisplayLayerProgress/"" target="_blank">DisplayLayerprogress</a>. This plugin is required and provides GCode parsing for Fan Speed, Layer/Height info and Average layer time. Is it installed, enabled and on the latest version?',
hide: false
});
return "Can't get stats from <a href='https://plugins.octoprint.org/plugins/DisplayLayerProgress/' target='_blank'>DisplayLayerprogress</a>. Is it installed, enabled and on the latest version?";
}
}

//Events from displaylayerprogress Plugin
Expand All @@ -50,22 +65,30 @@ $(function() {
if (data.fanspeed) { self.fanspeed(data.fanspeed); }
if (data.lastLayerDuration) { self.lastLayerDuration(data.lastLayerDuration); }
if (data.averageLayerDuration) { self.averageLayerDuration(data.averageLayerDuration); }
if (data.cpuPercent) { self.cpuPercent(data.cpuPercent); }
if (data.virtualMemPercent) { self.virtualMemPercent(data.virtualMemPercent); }
if (data.diskUsagePercent) { self.diskUsagePercent(data.diskUsagePercent); }
if (data.cpuTemp) { self.cpuTemp(data.cpuTemp); }
};

self.embedUrl = function() { //TODO: This is a hack. Should be replaced with the webcam view from the control page but I haven't succeeded yet.
if (self.settingsViewModel.settings.webcam) {
if (self.settingsViewModel.settings.webcam.streamUrl().startsWith("http")) {
return self.settingsViewModel.settings.webcam.streamUrl();
}
else {
return window.location.origin + self.settingsViewModel.settings.webcam.streamUrl()
}
}
else return "ERROR: Webcam utl not defined.";
}

self.getEta = function(seconds) {
dt = new Date();
dt.setSeconds( dt.getSeconds() + seconds )
return dt.toTimeString().split(' ')[0];
}

self.formatLayerAverage = function(timeString) {
timeString = timeString.replace("h", "");
timeString = timeString.replace("m", "");
timeString = timeString.replace("s", "");
timeString = timeString.replace("0:", "00:");
return timeString;
}

self.formatFanOffset = function(fanSpeed) {
fanSpeed = fanSpeed.replace("%", "");
fanSpeed = fanSpeed.replace("-", 1);
Expand Down
25 changes: 22 additions & 3 deletions octoprint_dashboard/templates/dashboard_settings.jinja2
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<h3>{{ _('Dashboard Settings') }}</h3>
<form class="form-horizontal">

<div class="control-group">
<label class="control-label">{{ _('Progress gauge type')}}</label>
<div class="controls">
Expand All @@ -13,8 +12,7 @@
</div>
</div>


<h4>{{ _('Temperature Gauge Maximums') }}</h4>
<legend>{{ _('Temperature Gauge Maximums') }}</legend>
<div class="control-group">
<label class="control-label">{{ _('Hotend Max:')}}</label>
<div class="controls">
Expand All @@ -33,6 +31,27 @@
</div>
</div>

<legend>{{ _('Widget Visibility') }}</legend>
<div class="control-group">
<div >
<label class="checkbox">{{ _('Show Webcam') }}
<input type="checkbox" data-bind="checked: settings.plugins.dashboard.showWebCam">
</label>
</div>
<div >
<label class="checkbox">{{ _('Show Fan Gauge') }}
<input type="checkbox" data-bind="checked: settings.plugins.dashboard.showFan">
</label>
</div>
<div >
<label class="checkbox">{{ _('Show Octoprint Host System Info') }}
<input type="checkbox" data-bind="checked: settings.plugins.dashboard.showSystemInfo">
</label>
</div>


</div>


</form>

Expand Down
43 changes: 30 additions & 13 deletions octoprint_dashboard/templates/dashboard_tab.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
<!-- HTML grid-based layout. Set to 3 columns wide in css -->
<div class="dasboardGridContainer">

<!-- System Info-->
<div class="dashboardGridItem" data-bind="visible: settingsViewModel.settings.plugins.dashboard.showSystemInfo()">
<img class="dashboardIcon" title="CPU Usage" src="/plugin/dashboard/static/img/cpu-icon.png"></img>
<div class="inline">
<span id="profileInfo" data-bind="attr: { title: 'CPU Usage' }, html: cpuPercent() + '%'"></span>
<span class="dashboardSmall" id="cpuTemp" data-bind="attr: { title: 'CPU Temperature' }, html: cpuTemp() + '°C'"></span>
</div>
</div>
<div class="dashboardGridItem" data-bind="visible: settingsViewModel.settings.plugins.dashboard.showSystemInfo()">
<img class="dashboardIcon" title="RAM Usage" src="/plugin/dashboard/static/img/ram-icon.png"></img>
<span id="connectionInfo" data-bind="attr: { title: 'Ram Usage' }, html: virtualMemPercent() + '%'"></span>
</div>
<div class="dashboardGridItem" data-bind="visible: settingsViewModel.settings.plugins.dashboard.showSystemInfo()">
<img class="dashboardIcon" title="Disk Usage" src="/plugin/dashboard/static/img/hdd-icon.png"></img>
<span id="stateInfo" data-bind="attr: { title: 'Disk Usage' }, html: diskUsagePercent() + '%'"></span>
</div>

<!-- Current Profile, Connection state, and current status-->
<div class="dashboardGridItem">
<img class="dashboardIcon" title="Printer profile" src="/plugin/dashboard/static/img/printer-icon.png"></img>
Expand All @@ -23,9 +40,9 @@
<img class="dashboardIcon" title="Hotend temp (target temp)" src="/plugin/dashboard/static/img/hotend-icon.png"></img>
<svg xmlns="http://www.w3.org/2000/svg" height="120" width="120" viewBox="0 0 200 200">
<path class="bg" stroke="#ccc" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none"/>
<path class="dashboardGauge" stroke="#09c" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': $parent.formatTempOffset(actual(), 300) }"/>
<path class="target" stroke="gray" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': $parent.formatTempOffset(target(), 300) }"/>
<text class="dashboardGauge" font-size="30" x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: Math.round(actual()) + '°C'"></text>
<path class="dashboardGauge" stroke="#09c" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': $parent.formatTempOffset(actual(), $parent.settingsViewModel.settings.plugins.dashboard.hotendTempMax()) }"/>
<path class="target" stroke="gray" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': $parent.formatTempOffset(target(), $parent.settingsViewModel.settings.plugins.dashboard.hotendTempMax()) }"/>
<text class="dashboardGauge" font-size="30" x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: Math.round(actual()) + '°C'"></text>
<text class="dashboardGauge" font-size="20" x="50%" y="65%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: name()"></text>
<text class="dashboardGauge" font-size="20" x="50%" y="85%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: 'Target: ' + target() + '°C' "></text>
</svg>
Expand All @@ -37,8 +54,8 @@
<img class="dashboardIcon" title="Bed temp (target temp)" src="/plugin/dashboard/static/img/bed-icon.png"></img>
<svg xmlns="http://www.w3.org/2000/svg" height="120" width="120" viewBox="0 0 200 200">
<path class="bg" stroke="#ccc" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none"/>
<path class="dashboardGauge" stroke="#09c" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': formatTempOffset(temperatureModel.bedTemp.actual(), 300) }"/>
<path class="target" stroke="gray" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': formatTempOffset(temperatureModel.bedTemp.target(), 300) }"/>
<path class="dashboardGauge" stroke="#09c" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': formatTempOffset(temperatureModel.bedTemp.actual(), settingsViewModel.settings.plugins.dashboard.bedTempMax()) }"/>
<path class="target" stroke="gray" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': formatTempOffset(temperatureModel.bedTemp.target(), settingsViewModel.settings.plugins.dashboard.bedTempMax()) }"/>
<text class="dashboardGauge" font-size="30" x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: Math.round(temperatureModel.bedTemp.actual()) + '°C'"></text>
<text class="dashboardGauge" font-size="20" x="50%" y="85%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: 'Target: ' + temperatureModel.bedTemp.target() + '°C' "></text>
</svg>
Expand All @@ -49,15 +66,14 @@
<img class="dashboardIcon" title="Chamber temp (target temp)" src="/plugin/dashboard/static/img/chamber-icon.png"></img>
<svg xmlns="http://www.w3.org/2000/svg" height="120" width="120" viewBox="0 0 200 200">
<path class="bg" stroke="#ccc" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none"/>
<path class="dashboardGauge" stroke="#09c" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': formatTempOffset(temperatureModel.chamberTemp.actual(), 300) }"/>
<path class="target" stroke="gray" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': formatTempOffset(temperatureModel.chamberTemp.target(), 300) }"/>
<text class="dashboardGauge" font-size="30" x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: Math.round(temperatureModel.chamberTemp.actual()) + '°C'"></text>
<path class="dashboardGauge" stroke="#09c" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': formatTempOffset(temperatureModel.chamberTemp.actual(), settingsViewModel.settings.plugins.dashboard.chamberTempMax()) }"/>
<path class="target" stroke="gray" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" stroke-dasharray="350" data-bind="attr: { 'stroke-dashoffset': formatTempOffset(temperatureModel.chamberTemp.target(), settingsViewModel.settings.plugins.dashboard.chamberTempMax()) }"/> <text class="dashboardGauge" font-size="30" x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: Math.round(temperatureModel.chamberTemp.actual()) + '°C'"></text>
<text class="dashboardGauge" font-size="20" x="50%" y="85%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="html: 'Target: ' + temperatureModel.chamberTemp.target() + '°C' "></text>
</svg>
</div>

<!-- Fan Speed -->
<div class="dashboardGridItem" data-bind="visible: temperatureModel.isOperational()">
<div class="dashboardGridItem" data-bind="visible: temperatureModel.isOperational() && settingsViewModel.settings.plugins.dashboard.showFan()">
<img class="dashboardIcon" title="Fan Speed" src="/plugin/dashboard/static/img/fan-icon.png"></img>
<svg xmlns="http://www.w3.org/2000/svg" height="120" width="120" viewBox="0 0 200 200">
<text class="dashboardGauge" font-size="30" x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="#08c" data-bind="attr: { title: 'Fan Speed' }, html: fanspeed"></text>
Expand Down Expand Up @@ -131,13 +147,14 @@
</div>
<div class="dashboardGridItem" data-bind="visible: printerStateModel.isPrinting()">
<img class="dashboardIcon" title="Layer Time" src="/plugin/dashboard/static/img/layer-time-average-icon.png"></img>
<span id="averageLayerDuration" data-bind="attr: { title: 'Average Layer Time' }, html: formatLayerAverage(averageLayerDuration())"></span>
<span id="averageLayerDuration" data-bind="attr: { title: 'Average Layer Time' }, html: averageLayerDuration()"></span>
</div>

</div>

<!-- Warning if DisplayLayerProgress is not installed -->
<span id="warn" data-bind="attr: { title: 'Warn' }, html: DisplayLayerProgressAvailable()"></span>



<br/>
<div id="webcamstreamer_wrapper" data-bind="visible: settingsViewModel.settings.plugins.dashboard.showWebCam()">
<iframe data-bind="attr: {src: embedUrl()}" frameborder="0" allowfullscreen></iframe>
</div>
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
plugin_name = "OctoPrint-Dashboard"

# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
plugin_version = "1.1.0"
plugin_version = "1.2.0"

# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
# module
Expand All @@ -33,7 +33,7 @@
plugin_license = "AGPLv3"

# Any additional requirements besides OctoPrint should be listed here
plugin_requires = []
plugin_requires = ["psutil"]

### --------------------------------------------------------------------------------------------------------------------
### More advanced options that you usually shouldn't have to touch follow after this point
Expand Down

0 comments on commit ebc211a

Please sign in to comment.