🎇 FlexDash alpha release - round 2

Hi @tve
Just reworking my flexdash dashboard (again!), and taking advantage of the 'Widget Array' feature for the 'Stat' widget.
I've created a panel sized 1 x 8, added a stat node, injecting 3 topics, all good so far.
But I want to move the panel to the top of my grid, so I select Edit, click the pencil icon, and do not see the floating options (it appears momentarily).
The options work fine with the other widgets.

It's not a big problem because I can simply move the other widgets down, but wondered if you were aware.

flex

1 Like

Works fine for me :crazy_face:
Can you look into the browser console for any errors/warnings?

No, and none in the node-RED log either.
I've just updated the gif above, which shows the 'options' appearing momentarily, before quickly sliding off to the left.
I'm using your latest version.

I believe I saw the flash-and-gone one time here while clicking around but I can't repro. Try resizing the panel in the flow editor, e.g. much taller or so. Or try resizing a widget in the row above to be much taller. This is frustrating...

Yep! I've tried various things, including your last suggestions, and sometimes options appears, and sometimes not, but it's not repeatable.
Maybe similar to the previous post????

It's not a big problem because we can move other widgets up or down to compensate, let's see if a consistent pattern emerges that helps solve the problem.

PS. The 'Widget Array' feature will simplify my flow significantly, nice work :smiley:

1 Like

A few months ago, I created a lighting controller in Flexdash, which worked great. Any button that I clicked would retain it's state if viewed on a different browser, if I refreshed or reopened a closed browser, even days later. So if I activated a light on with my desktop, I could check on my phone at a later time, see it's status and maybe switch it off.
But.. I don't know if it's a consequence of;

But now, the button states no longer persist, so as soon as the browser is closed, refreshed, or a different browser the visible state of the button is sadly gone :astonished:

buttons

Here is a mock up flow to demonstrate -

[{"id":"43a8db52e4b4095f","type":"function","z":"1543d308b342690a","name":"flexdash button","func":"if (msg.payload == 'OFF') {\n    msg.color = 'green-lighten-3'\n    context.set('currentState', msg.color)\n} else if (msg.payload == 'ON') {\n    msg.color = 'yellow-accent-3'\n    context.set('currentState', msg.color)\n} else {\nconst state = context.get('currentState') || 'green-lighten-3'\nlet newState = (state == 'green-lighten-3') ? 'yellow-accent-3' : 'green-lighten-3'\ncontext.set('currentState', newState)\nmsg.color = newState\n}\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":565,"y":2155,"wires":[["453a4dee8be38cc3"]]},{"id":"e957db9e5704ea77","type":"fd-push-button","z":"1543d308b342690a","fd_container":"dc6f6a921f8e0373","fd_cols":"4","fd_rows":1,"fd_array":false,"fd_array_max":10,"fd_output_topic":"","name":"Driveway Lights","title":"Driveway","popup_info":"","enabled":true,"color":"","output_value":1,"icon":null,"x":380,"y":2155,"wires":[["43a8db52e4b4095f"]]},{"id":"5ab534839435aa10","type":"fd-push-button","z":"1543d308b342690a","fd_container":"dc6f6a921f8e0373","fd_cols":"4","fd_rows":1,"fd_array":false,"fd_array_max":10,"fd_output_topic":"","name":"Garden Lights","title":"Garden","popup_info":"","enabled":true,"color":"","output_value":1,"icon":null,"x":380,"y":2230,"wires":[["03604196c56471e5"]]},{"id":"69f2bed6628bcbfb","type":"fd-push-button","z":"1543d308b342690a","fd_container":"dc6f6a921f8e0373","fd_cols":"4","fd_rows":1,"fd_array":false,"fd_array_max":10,"fd_output_topic":"","name":"Security Lights","title":"Security","popup_info":"","enabled":true,"color":"","output_value":1,"icon":null,"x":380,"y":2305,"wires":[["314386bde8e02957"]]},{"id":"d492601966ea1090","type":"fd-push-button","z":"1543d308b342690a","fd_container":"dc6f6a921f8e0373","fd_cols":"4","fd_rows":1,"fd_array":false,"fd_array_max":10,"fd_output_topic":"","name":"All off","title":"All Lights OFF","popup_info":"","enabled":true,"color":"purple-lighten-3","output_value":"OFF","icon":null,"x":120,"y":2255,"wires":[["5f525de183fd14f5"]]},{"id":"c820172e969b0af4","type":"fd-push-button","z":"1543d308b342690a","fd_container":"dc6f6a921f8e0373","fd_cols":"4","fd_rows":1,"fd_array":false,"fd_array_max":10,"fd_output_topic":"","name":"All off","title":"All Lights on","popup_info":"","enabled":true,"color":"purple-lighten-3","output_value":"ON","icon":null,"x":120,"y":2220,"wires":[["5f525de183fd14f5"]]},{"id":"03604196c56471e5","type":"function","z":"1543d308b342690a","name":"flexdash button","func":"if (msg.payload == 'OFF') {\n    msg.color = 'green-lighten-3'\n    context.set('currentState', msg.color)\n} else if (msg.payload == 'ON') {\n    msg.color = 'yellow-accent-3'\n    context.set('currentState', msg.color)\n} else {\nconst state = context.get('currentState') || 'green-lighten-3'\nlet newState = (state == 'green-lighten-3') ? 'yellow-accent-3' : 'green-lighten-3'\ncontext.set('currentState', newState)\nmsg.color = newState\n}\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":570,"y":2230,"wires":[["b71c85784cc7779a"]]},{"id":"314386bde8e02957","type":"function","z":"1543d308b342690a","name":"flexdash button","func":"if (msg.payload == 'OFF') {\n    msg.color = 'green-lighten-3'\n    context.set('currentState', msg.color)\n} else if (msg.payload == 'ON') {\n    msg.color = 'yellow-accent-3'\n    context.set('currentState', msg.color)\n} else {\nconst state = context.get('currentState') || 'green-lighten-3'\nlet newState = (state == 'green-lighten-3') ? 'yellow-accent-3' : 'green-lighten-3'\ncontext.set('currentState', newState)\nmsg.color = newState\n}\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":570,"y":2305,"wires":[["e98ec8288bdbfcd8"]]},{"id":"453a4dee8be38cc3","type":"junction","z":"1543d308b342690a","x":370,"y":2200,"wires":[["e957db9e5704ea77"]]},{"id":"b71c85784cc7779a","type":"junction","z":"1543d308b342690a","x":370,"y":2275,"wires":[["5ab534839435aa10"]]},{"id":"e98ec8288bdbfcd8","type":"junction","z":"1543d308b342690a","x":370,"y":2350,"wires":[["69f2bed6628bcbfb"]]},{"id":"5f525de183fd14f5","type":"junction","z":"1543d308b342690a","x":204,"y":2236,"wires":[["22f85dc3b60161fa","f1e1615673248a33","6cdb4f2c79162ecb"]]},{"id":"22f85dc3b60161fa","type":"junction","z":"1543d308b342690a","x":305,"y":2130,"wires":[["fdbf0ff17b8f81b7"]]},{"id":"f1e1615673248a33","type":"junction","z":"1543d308b342690a","x":305,"y":2205,"wires":[["2e79537acafd90ea"]]},{"id":"6cdb4f2c79162ecb","type":"junction","z":"1543d308b342690a","x":305,"y":2280,"wires":[["4057df264ff384ed"]]},{"id":"4057df264ff384ed","type":"junction","z":"1543d308b342690a","x":445,"y":2280,"wires":[["314386bde8e02957"]]},{"id":"2e79537acafd90ea","type":"junction","z":"1543d308b342690a","x":445,"y":2205,"wires":[["03604196c56471e5"]]},{"id":"fdbf0ff17b8f81b7","type":"junction","z":"1543d308b342690a","x":450,"y":2130,"wires":[["43a8db52e4b4095f"]]},{"id":"dc6f6a921f8e0373","type":"flexdash container","name":"Example panel","kind":"Panel","fd_children":",7b774415d2399f26,fa5803caa1b3f2d5,ec1cf605133c47cd,f0d58cbb8abe635f,1c3e766d6d24ef84,e957db9e5704ea77,5ab534839435aa10,69f2bed6628bcbfb,d492601966ea1090,c820172e969b0af4","title":"","tab":"fef6c0f6d48841d6","min_cols":"1","max_cols":"20","parent":"2fa703121bdc84d2","solid":true,"cols":"1","rows":"5"},{"id":"fef6c0f6d48841d6","type":"flexdash tab","name":"Energy","icon":"mdi-home-lightning-bolt-outline","title":"","fd_children":",69c2e3f5798c3475","fd":"e8f5aea52ab49500"},{"id":"2fa703121bdc84d2","type":"flexdash container","name":"Examples grid","kind":"StdGrid","fd_children":",dc6f6a921f8e0373","title":"Examples","tab":"6dfcd673f5649409","min_cols":"1","max_cols":"20","parent":"","solid":false,"cols":"1","rows":"1"},{"id":"6dfcd673f5649409","type":"flexdash tab","name":"Examples","icon":"mdi-view-dashboard","title":"Example flows","fd_children":",2fa703121bdc84d2","fd":"e8f5aea52ab49500"}]
1 Like

Yes! Your suspicion is correct and I didn't realize the change in behavior when I released or I would have done something different. My apologies for messing up.

What's happening is that widget output messages has a msg._fd_socket field and widget inputs act on the same field. So if you connect an output message to an input you will only affect the specific browser tab that produced the output. To affect all browsers (and future ones) you need to delete the _fd_socket field.

As a twist, if you use the internal loopback (checkbox on the general tab) then that always affects all browsers (i.e. ignores the internal _fd_socket).

I know I have work to do to make all this more obvious/usable. I didn't realize that what I pushed was going to mess something up.

1 Like

It's in alpha, I expect things to get broken :laughing:
Now it's broken, it's an opportunity for me to look at things again, and make my flows better.

1 Like

Oh, just to be clear, what you need to do is:

For input widgets whose state reflects global state, i.e., they should have the same state in all open browsers, you should either use the built-in loopback option or you should delete the _fd_socket field from messages fed into the widget.

What is new is that you can have per-browser tab UI. For example, say you have a toggle to show active vs. inactive gizmos in a table. You want each viewer to choose which to show. Then you use the output of the toggle to put the data for the table together and you inject that into the table with the same _fd_scoket. To see the toggle state you need to use an external loopback keeping the _fd_socket.

Update:
My plan to make this a little mistake-proof is to add a setting to grids with the following options:

  • disallow _fd_socket: drop messages with _fd_socket in all widgets of this grid and produce an error message, this is the default setting
  • allow _fd_socket: allow messages with and without _fd_socket in all widgets of this grid
  • require _fd_socket: drop messages without _fd_socket in all widgets of this grid and produce an error message

This means that by default all state is global. The "require _fd_scoket" setting is useful if you have a grid with stuff you always only want to show per-browser and you want to ensure no global changes sneak in by mistake. I'm not sure it's practical, may need some tweaking, e.g. to allow an initial global message.

1 Like

Already fixed thanks.

Sounds good :+1:
Keeping 'all state as global' as a default will create a good starting point for users.

1 Like

Edit panel of grid config nodes now has:

And error messages like:
image

(My test grids are called "unicast" and "broadcast" which makes the error message ending sound a bit strange.)

:rocket:

I'm also making progress on the FlexDash ctrl node to receive browsing updates and control some internal state:

Node to observe and control some internal FlexDash state, such as currently shown tab as well as features of tabs, grids, and panels.

Output messages are generated for a number of events with the following payload:

  • type: the type of event.
  • cause: user/message for events that can be caused by user action vs. a message.
  • name: the name of the node representing the FlexDash element associated with the event.
  • node_id: the ID of the node representing the FlexDash element associated with the event.
  • title: the title of the FlexDash element associated with the event.
  • icon: the icon name of the FlexDash element associated with the event (for tabs only).

Also, msg._fd_socket identifies the browser tab.

The events types are:

  • new client: a new browser tab has connected, msg.browser is an ID assigned to the the browser using a cookie, it is the same for all tabs of the browser.
  • idle client: a browser tab has disconnected and not reconnected in a few seconds
    (it may reconnect an unbounded time later).
  • change tab: the tab shown to the user has changed. msg.cause is user, message, hide, or expose. The last two refer to the browser window becoming invisible and visible again due to the user changing browser tab, minimizing the window, etc.

In the case of cause==hide the fields identifying the tab will be null/missing.

  • close grid: the pop-up grid has been closed or the regular grid has been rolled-up.
  • open grid: the regular grid has been rolled-down.

Input messages must have a msg.action field and they must target a component by setting either msg.name, msg.node_id, msg.title, or msg.icon (see output messages for details).

The set of actions is:

  • change tab: change the tab to the one targeted.
  • edit: edit the targeted component, setting one or multiple properties to the corresponding values in msg. Supports tabs, grids, and panels. Grids support an additional show property which rolls up/down standard grids and pops up or hides pop-up grids.

Note that the msg._fd_socket is supported and can be used to target individual browser tabs.

3 Likes

I'm probably getting over-excited and am as usual over-optimistic, but are we approaching beta yet? To me beta means that the functionality has stabilized, i.e., big breaking changes are not expected. There is still a list of open issues but they're narrow is scope and have work-arounds.

To start, I updated the list of issues posted oct 9th:

  • Toggle and other input feedback/loopback has to go through server
  • Sending one value to plots or sparkline doesn't work when browser is refreshed, need to fix implementation :stop_sign:
  • Log line "Sending config to ... from store undefined" is confusing
  • Panel shows a ripple on click, it shouldn't.
  • TimePlot is blank if both axes are used and the data for one of the axes is all null (prob. uPlot bug)
  • TimePlot should support SI prefixes
  • When there are many time plots on a page the tooltip can appear in the wrong plot (needs clean repro)
  • TimePlot: remove circles for data points when there are few points
  • TimelinePlot needs to be clickable
  • TimelinePlot: look into supporting multi-line labels
  • TextView: provide option to wrap text, also to scroll to bottom
  • Button: look into 100% width option as well as FAB support
  • Figure out support for per-connection data (not released yet)
  • Support simple login
  • Option to hide edit mode
  • Maximized plots that have no title end up w/out close button (needs repro)
  • Allow tables to be magnified full-page
  • Implement a search in SimpleTable
  • Fix initial "expose" tab-change event on intial page load
  • Look into socket.io inactivity timeout on server end
  • Think about arrays and per-socket data 'cause array topics are global, e.g. delete-array-element is global
  • Document the fact that internal loopback is broadcast, or rethink the functionality
  • Use material design color palette for graphs to unify color names
  • Roll NumberField functionality into Stat widget and perhaps TextField into Label. :stop_sign:
  • Edits in the live dashboard get lost if a related edit panel is open in the flow editor and is subsequently saved
  • Implement UI to reorder grids on a page :stop_sign:
  • Rethink editing in live dashboard: could reordering/resizing be done nicely without? :stop_sign:
  • Triple-check implementation of "node is truly deleted" in node-red-flexdash and actually delete references :stop_sign:

Keen observers will notice that the list has gotten longer :roll_eyes: but I think the impact of the items has narrowed. Most fall into the category of bug fixes or enhancements to one or two components, which is something compatible with being in beta. I marked the items I think need to be addressed before declaring beta with a stop sign. Thoughts?

The one big item that is not on the list is a big refactoring to support arbitrary nesting of container components in the dashboard. I need to think about it some more, but I have the feeling it would be a big stability set-back at this point and better reserved for FlexDash V2. I believe it can be done without big impact on users.

1 Like

Imho if you are happy with the global design, and your design is capable to implement most of the (currently known) required features, and you don't expect breaking changes anymore soon. Then indeed I would go to beta phase.

A big applause and thank-you-for-all-the-hard-work-for-this-community :partying_face: :champagne: :clinking_glasses:

But unfortunately I think you forgot a very difficult item on your TODO list: convince the developers of the old UI nodes, that they should really start find some free time to start migrating their stuff to Flexdash :wink:

3 Likes

Heh, the old timers are welcome to stick to the ui nodes :joy:
Maybe in a bit some of us can get together and do a "node of the week" thing: have users propose "if FlexDash had node X I would migrate", then pick one a week, and implement it. :boom:

NB: I have lots of stuff in FD I'd like to redo & refactor, but if I start it's never gonna get to release state...

1 Like

Trust me - I don't need convincing... just time... someone still has to keep an eye on the old code... hopefully I may get some time over the holidays to dip my toes in.

6 Likes

Hmmm, you're likely to spot stuff that needs fixing, I look forward to hearing about it!

Do you think it is useful to create a separate thread about ui node migration, from old dashboard to flexdash? We had some discussions about that some time ago. But perhaps you have gained new insights, during your journey...

Where the data is stored, how updates of that data need to be done, how to reload that state after a restart, and so on...

1 Like

I think it's premature to talk a lot about migrating nodes, or at least, I don't have the bandwidth to do so. If anyone has a node they'd like to migrate I'm more than happy to help if they start a thread and flag me.


Following my post How to track dependencies between nodes I now have one more item for my to-do list :

  • remove all editing from FlexDash and implement live reordering/resizing in the flow-editor...
1 Like

Thanks for your work on FlexDash, I will try and keep my questions here short and to the point as I value your time to give some input.

My Node-RED dashboard has around 1000 users a day.
It uploads around 1Tb of data since any one page loads the whole site.
(Painful thread here: How to make Dashboard webserver more robust?)

FlexDash quesitons:

  1. Does loading one FlexDash page load the whole site?
  2. Does FlexDash use ExpressJS / Socket.io? (From the above link, I'm led to believe this is a bad thing for me if it does).
  3. Is FlexDash multi-user aware? ie If one person does a search does everyone get those results showing up everywhere like the current dashboard?
  4. I think I can run FlexDash and the stock dash together? ie, I cant take my current site down while I migrate to the new dash. I need the same URL path but different tabs for example.

Thanks for your help.

Ben

[quote="thebaldgeek, post:140, topic:67783"]

  • Does loading one FlexDash page load the whole site?

Yes & no. All the widget config is loaded but if you have pages (tabs) that have particularly heavy data (e,g, graphs with lots of data) then with the next release you could send that on-demand when the user navigates to that tab.

  • Does FlexDash use ExpressJS / Socket.io? (From the above link, I'm led to believe this is a bad thing for me if it does).

Yes it does. Could you elaborate on what the problem is? (I'll check the link)

  • Is FlexDash multi-user aware? ie If one person does a search does everyone get those results showing up everywhere like the current dashboard?

With the last release it is multi-user aware and with the next release that will be fully usable. I need this for exactly the reason you allude to.

  • I think I can run FlexDash and the stock dash together? ie, I cant take my current site down while I migrate to the new dash. I need the same URL path but different tabs for example.

FlexDash can run on the NodeRED server mounted at an arbitrary URL or on a separate port at the root. So yes, you should be able to run the two side-by-side (and you can iframe std dashboard tabs into FlexDash tabs pretty seamlessly).

I'm happy to answer more questions here, but if you'd like to dive in deeper just open a separate thread and @tve.