I think that looks great. And if your configuration allows users to turn features off like tabs, and what data to show, then everyone will be happy.
I'd be exceedingly happy if I could just use more horizontal real estate in my browser window.
Search the forum here for "pimp".
I have made a quick and dirty POC based on FreeBoard.io now. This is just to illustrate some of the concepts from my original post.
This first video shows how to add a custom data source and create a basic dashboard from scratch. The data source is in this case a small web service that exposes some PSUtil metrics over a rest interface on localhost but It can in theory be another plugin or the OctoPrint API (shown in the next video). I also created a couple of widgets from the data source and arranged them as a dashboard.
This custom dashboard can be saved and loaded programatically but I haven't implemented that yet.
In this video, I show how to connect to the OctoPrint API and create a hotend gauge widget.
And here is the third and final video where I load an existing dashboard that connects to the Printer, Job and Connection endpoints of the OctoPrint API. It also contains a set of host widgets from the first video. I also show how the user can re-arrange the dashboard(s) to their taste.
I even managed to get the webcam view to work thru the image widget:
I think this demonstrates the level of flexibility I'm after. Any thoughts?
I have made some progress and more or less settled on using Packery for the layout. I've experimented some more and I now have the bits and pieces I think I need to stitch this together.
I haven't made any actual widgets yet (except a webcam and some dummies) but I can at least create a new dashboard, add widgets thru both UI and settings, It is now possible to rearrange the widgets using drag&drop and the layout is persisted in the backend across devices/sessions. It is also possible to set widget sizes at creation time.
I think I will at least provide the following types of data sources:
- Dash (what the plugin exposes natively)
- Event (anything exposed via the event manager)
- JSON
The learning curve is quite steep for me so this will take some time...
Looks good, very nice progress.
One thing I thought might be a useful. It's more of an extension on the "Drill-down" dashboards. Have you thought about drilling-up?
There are many users of octoprint that run multiple printers, so it would be of use to have a basic dashboard (% complete, ETA, etc) for each printer on one single dashboard page. You could click on a printer displayed to be taken to the actual octoprint instance and dashboard for the printer selected.
Thanks for the feedback! I hadn't thought of that but I think that could work out of the box if i succeed with this (and add a hyperlink to widgets). Since one data source type is JSON you could just connect widgets to the API of another OctoPrint server to get the available metrics out of it.
I've made some more progress now but I have encountered a slight problem.
The jinja template creates a dasboard by reading widget settings like this:
<div class="grid" data-bind="foreach: settingsViewModel.settings.plugins.dash.dashboards">
<div class="grid-sizer"></div>
<!-- ko foreach: widgets -->
<div class="widget w1 h1" data-bind="appendWidget: $data, attr: {'widget-id': id}, text: name"></div>
<!-- /ko -->
</div>
I have created a custom knockout binding handler "appendWidget" like this:
ko.bindingHandlers.appendWidget = {
init: function(element, valueAccessor) {
$grid.append( element ).packery( 'appended', element );
//Make it draggable
var draggie = new Draggabilly( element );
$grid.packery( 'bindDraggabillyEvents', draggie );
}
};
I think this is the right approach when injecting DOM elements and it almost works. The widgets are injected to the DOM and bound to the grid as expected. The problem is that i get a knockout error right after that:
packed_core.js?b00f909b:16147 Could not bind view model DashViewModel to target #tab_plugin_dash : Error: Unable to process binding "foreach: function(){return settingsViewModel.settings.plugins.dash.dashboards }"
Message: You cannot apply bindings multiple times to the same element.
I have no clue why this happens. Any ideas?
The source is available here: https://github.com/StefanCohen/Dash
For such cases I disabled the minified and bundled feature in my development-settings.
config.yaml
devel:
virtualPrinter:
enabled: true
forceChecksum: false
okAfterResend: false
okWitchLinenumber: false
webassets:
bundle: false
minify: false
So that I can debug the 3rd party sourcecode or I just add some console.info lines the the 3rd party sourcecode like knockout.js.
Hi @Stefan_Cohen,
I checked out your sourcecode and received a different message:
Could not bind view model DashViewModel to target #tab_plugin_dash : ReferenceError: Unable to process binding "foreach: function(){return settingsViewModel.settings.plugins.dash.dashboards }"
Message: Unable to process binding "foreach: function(){return widgets }"
Message: Unable to process binding "attr: function(){return {'widget-id':id} }"
Message: id is not defined
Ah. I have missed to update the the default config with a bunch of settings (including the ID).
it should look like this in config.yaml:
dash:
dashboards:
- name: main
widgets:
- animate: ''
enabled: true
id: '1'
link: somelink
name: first
src: https://s3-us-west-2.amazonaws.com/s.cdpn.io/82/one-world-trade.jpg
target: _blank
type: image
units: ''
value: somevalue
widgetSize: w1 h1
- animate: ''
enabled: false
id: '2'
link: ''
name: second
src: https://s3-us-west-2.amazonaws.com/s.cdpn.io/82/cat-nose.jpg
target: _blank
type: image
units: ''
value: ''
widgetSize: w2 h2
- animate: ''
enabled: false
id: '3'
link: ''
name: third
src: https://s3-us-west-2.amazonaws.com/s.cdpn.io/82/flight-formation.jpg
target: _blank
type: image
units: ''
value: ''
widgetSize: w1 h1
dataSources:
- enabled: true
headers:
- name: X-Api-Key
value: My API Key
- name: Test
value: Some value
method: get
name: OctoPrint
pollingInterval: 1
type: json
url: http://localhost/api/job
- enabled: true
name: System Clock
pollingInterval: 1
type: clock
hmm..I think the problem is the $grid-element append
, because you append into the foreach-section new elements by your own and this is what KO doesn't like.
If I put the grid identifier above the foreach-loops it works...kind of...in my case it adds three widgets and then I receive again an error-message.
<div class="grid">
<div data-bind="foreach: settingsViewModel.settings.plugins.dash.dashboards">
...
Maybe thats the right direction...maybe
Thanks. That is the same kind of error I get. I have a feeling that this has something to do with my binding handler.
I originally tried by just letting the template produce the widgets but I couldn't find a way to add them to the packery grid.
Then I looked at the afterRender method but I couldn't get it to work either. That's when I found this in the Docs: https://knockoutjs.com/documentation/template-binding.html#note-4-using-afterrender-afteradd-and-beforeremove
Sometimes you might want to run custom post-processing logic on the DOM elements generated by your templates. For example, if you’re using a JavaScript widgets library such as jQuery UI, you might want to intercept your templates’ output so that you can run jQuery UI commands on it to transform some of the rendered elements into date pickers, sliders, or anything else.
Generally, the best way to perform such post-processing on DOM elements is to write a custom binding,
It is working now...I have no idea if it should looks/act like this: three black boxes, draging around. But everything works without an error
I just moved the $grid outside the loop.
<div class="grid">
<div class="grid-sizer"></div>
</div>
<div data-bind="foreach: settingsViewModel.settings.plugins.dash.dashboards">
<div class="grid-sizer"></div>
<!-- ko foreach: widgets -->
<div class="widget w1 h1" data-bind="attr: {'widget-id': id}, appendWidget: $data, text: name"></div>
<!-- /ko -->
</div>
What kind of problem do you have with the "afterrender" approach? Because this sound like the right solution. You modify needed attributes via template that jquery needs (widget-id).
I have an other idea: build your on "afterRenderWidget"-function like this:
- separate creating the layout
- then assign jquery-functionality
<!-- do layouting -->
<div data-bind="foreach: settingsViewModel.settings.plugins.dash.dashboards">
<div class="widget w1 h1" data-bind="attr: {'widget-id': id} />
</div>
<!-- do assign jquery stuff -->
<div data-bind="assignWidgets: settingsViewModel.settings.plugins.dash.dashboards" style="display:none">
ko.bindingHandlers.assignWidgets = {
...
now you can iterate over the given array of all dashboards, grab the widgets via $ selector and add the
packery and Draggabilly (without append, because already there)
What do you think?
Thanks! I will try that right away.
Update: That first method worked but I think I will explore your second idea since I think it will work much better with multiple dashboards.
Update 2: Oh. I think I just realised what I did wrong. I'm calling .append from the binding handler but the widget is already there. I should just be able to call the packery "appended" method and then use your second idea with the hidden div to init the layout once all dashboards & widgets have been inserted on the page.