Some UIBuilder guidance?

Hello

I've finally got started with UIBuilder this week.

Unwisely, I looked through the docs just enough to appreciate how very well documented it is, then proceeded to completely ignore the excellent documentation and go straight to AI for help getting my app working. (I know - bad idea, need a kick up the bum, but just felt that if I could get something working without using my brain first, then it would be good enough exposure to commit to using it... which now I have!)

I recorded a 10 min video with some basic questions at the end and a request for general guidance. It's mostly me showing what I'm trying to do, and checking whether I'm going in the right direction before I attempt to scale up further!

If anyone has a moment to watch and comment back here on this thread with any tips or advice I'd appreciate it!

Link here: https://youtu.be/N6S2xxJKZB8

OK, I'm up to 3:12 in the video - don't think I'll be finishing this evening but a couple of quick comments up front.

  1. Start simple! The art of a custom dashboard is to build its pieces. Clearly, you want your outer structure for your lighting page to be probably a "flex" CSS layout. This is one of the easiest ways to have a layout that will automatically re-shape itself when the screen gets too narrow. I've previously posted some example layouts that will get you started with this.
  2. You've got a VERY complex set of rules that you are handling there. So now would be a good time to make sure that you can parameterise everything and end up with a bunch of reusable parts. That starts with the data and naming of things. You may already have this done which would be good.
  3. You can make use of HTML <template> tags in your HTML file if you find yourself needing common structural elements. You can then clone these into place and make any amendments (likely you will need to change ID's so you can do further processing, if using uibuilder's smart data tags or attributes, you will also want to change those - this can be a great way to parameterise things so that the page gets automatically updated using messages from Node-RED with minimal (often no) JavaScript.

If it were me starting from scratch, I would first get my general layout done and then create a simplistic template that can be cloned for each light. Then I would get a flow that sends all of the light data and some JavaScript to process that into each cloned template. I wouldn't worry about all the details until I'd done that.

Oh, before I forget, you will want to think about whether you want to stick with a Single Page App (SPA) style which would have been your only options for either of the Dashboards or whether you want a multi-page app (if, that is, you actually want more than a single page. :smiley: )

I'll try and look at the rest tomorrow.

Thanks! So you will see later in the vid that I've already done a lot in terms of getting it up and running: I have scenes and fixtures up and running, plus a nice floating context menu for managing scenes. Perhaps to allow for more useful feedback I should post code, although I don't expect others to trawl through my code!

Yes and so far I have one tag, the JS does everything else! Reason being (you'll see) I am allowing for multiple "cards" per page, so I don't think I can bring any more of the template into the HTML. I may be wrong.

Yes and that's one of my questions later in the vid :slight_smile:

Thanks ever so much for your input so far.

So, watching some more. At around 7:10 I can now see that you are using 3 separate uibuilder nodes. An interesting approach but I wouldn't do it that way myself. Mainly because it is simpler to have multiple pages under a single uibuilder node where you can more easily share code as needed. Shared code might be common CSS, common JS or even common HTML (uibuilder's front-end library should let you easily import external HTML files dynamically).

If you stick with separate uibuilder nodes, you can still get shared resources though. Either using UIBUILDER's common folder or using Node-RED's static folder. You could probably even use "linked" files if you wanted to.

In truth though, I probably would not even use multiple pages but rather have a single page where elements are shown and hidden using an on-page menu of links or buttons. This is a lot more efficient since you only have a single page with pre-loaded scripts/styles/etc. It uses a bit more browser memory but that will never be an issue here unless you are operating the lighting for a whole skyscraper!

This way means that you can easily re-group, create new groups, etc as desired. Simply by sending a new group config to the page.

As always, the trick to this is to make sure that you have a logical topic layout - just as you would for using MQTT. That way, your front end code can easily operate on the different topics as needed.

Question 1 answer:

Yes. 1 uibuilder node is a better way.

No, you don't need the other uibuilder templates. You are already ahead of what those templates can offer other than maybe if you wanted to move to the more modern front-end syntax using ES Modules and there really isn't a lot of need for you to do that, especially if you are sticking to native HTML/CSS/JS that you've created yourself. If ever you do want to switch, I would temporarily add a new uibuilder node, try out the ESM template and then migrate your own code to use the same features. The other templates are mostly for historic reasons where people wanted to use Svelte or VueJS and I think you've already seen that they really aren't needed for modern websites (with the possible exception where you are building a really large, complex web app as part of a commercial team).

Regarding scaling, the thing to do before starting to scale to other controls would be to make sure that you run through the code and make sure that it uses DRY (Don't Repeat Yourself) principles. For example, if you have the same JavaScript code in >1 place , that should be a function. HTML repeated in >1 place should be a template and CSS in >1 place should be restructured to use multiple classes. Sorting this now will really help as you move forwards.

Incidentally, when using AI for coding, having some standard rules really helps. Telling the AI what style of coding you want, that you want to maximise code reuse and use DRY, etc. Also, telling it to add JSDoc comments to code will help you make sense of everything, especially when you come back to it in 6 months.

Question 2 answer:

As already mentioned, a single uibuilder node with a single page is likely to be a better architecture for you. You could always split things later but most of what you are doing is using a single element repeated across multiple devices. So having everything in a single HTML, at least for now, is almost certainly going to be easier to scale.

At some point, you may decide to expand your dashboard to include some very different things such as weather forecasts, local bus timetable, etc. At that point, you may wish to consider using a true SPA using the uib-router library. As in this example from my live system:


By the way, don't forget the many example flows that are available in Node-RED:

It is a good idea to create one or more test uibuilder nodes so that you can have a play with things.

By the way, you will see that there is no "Right" way to use uibuilder. The best way is the way that works for you. Some try to minimise front-end code, others will embrace it. Neither way is wrong. Just follow normal good practice:

  • Don't repeat yourself - that's what computers are best at after all, make them do the work.
  • Don't assume you will understand what you did last time! The opposite is normally true so make sure that you put liberal amounts of comments in place to explain things. It isn't wrong to have nearly as many comment lines as code and will likely save you enormous amounts of time and heartache in the future. Remembering that code will be read many (hundreds sometimes) times more than it is ever written.
  • Keep it simple. Complexity is easily added but is hard to remove.
  • Have fun! Probably the most important principle of all, why do it otherwise. :smiley:

The most amazing thing I've discovered in the building of uibuilder is just how much can easily be achieved using some HTML, CSS and a smattering of JavaScript.

How are you chatting to your TP-Link Switch? (or is it just a ping - I am assuming not (dangerous) as you specifically mention that for the heating control)

This should give you a clue. :smiley:

The network links in that display are all pings only. Just lets me know they are reachable.

I didn't show more of that page

Each entry may have one of a number of applied tests. The services use MQTT entries provided by Telegraf and show whether the service is actually live.

The home automation section have more direct MQTT and the websites have a periodic check to make sure they are returning the expected content.

This is driven by the following flow:

The top section provides the live data by creating a set of MQTT subscribes from a global context var that defines everything.

The bottom section creates the static HTML for the page based, again, on that same context var. This means that the page only gets re-created if I need to change the entries - which is very rare. To change them, I simply update the var and manually run the lower flow followed by the upper one. Node-RED restarts don't need a manual run.

There are other flows on the same page, grouped by SPA route that control the inputs and outputs for that route. A single uibuilder node is all that is needed.


(The node with the cross is a sub-flow that blanks out the current message properties).

Here is one of the other routes. Showing walks near me. The data comes from a weekly email from OutdoorActive who's app I use. I have a flow that grabs and processes the data into another global context var and files the email for future reference. Activating the route, simply re-sends the data down and the table is built on the fly. Each entry can be expanded to show details that have been scraped from the website.

Thank you @TotallyInformation for the advice and guidance getting started. I've consolidated everything into a single uibuilder node now, and next I plan to get some URL params working e.g.
10.1.1.30:8000/dash?ctrl=light&zones=kitchen,living,dining

It's a bit of a journey and I completely agree with your points about directing AI towards a style of coding. I generally write code in a really basic procedural-like way and I request AI to always produce code that is very simple for my brain! I have this idea that one day I won't be able to use AI in the same way as I did (maybe it will move behind paywalls etc.) and I want to be able to decode my own code later...

Cheers

Mat

No problem.

Yes, I forgot to mention the url params. An alternative way to control the visible elements with the advantage that you can bookmark and share things.

Well, that's the fun part isn't it. :smiley:

This is a sensible approach. It is helped by making the AI add the JSDoc comments. I am forever having to re-learn my own code even without AI having written it. Much of the behind-the-scenes uibuilder code is just that long and complex and it has evolved over more than a decade so no way I can remember even a fraction of it. Without comments and documentation, it would be pretty impossible to keep moving forward at pace.

And AI has already tipped over an edge for me at least. Not sure if you see the lounge posts but I'm keeping track there of my move from VSCode with GitHub Copilot for AI (which was already £100 a year before the prices exploded this month) to VSCodium (the open version of VSCode) and the continue AI extension coupled mainly to OpenRouter. While this is a lot more complex to set up, it has brought my costs actually a fraction of what I was paying. I've been on that for a month now and (although admitedly I've not done so much coding this month) only spent $1! The real test though will be when I pick up the pace of coding after the summer.

And that is a rule to live by for sure.

Well after what seems like a week of solid coding (ok I throw that word around a bit too casually, it has been a week fighting with an LLM to code at scale on my behalf!) I have a learnt a LOT about SPA architecture and basically pushed the limit of what you can do without a framework. (None used here.)

Here's my dashboard, in case anyone is interested. It's fully operational.

  • swipe-left menu at the top to browse to different areas (selections of zones). No swiping needed on this view, but you can imagine on an iPhone it's useful.
  • Active Zones - special view that redirects to a URL with sorted list of zones e.g.

/dash?cards=lg_living.lighting,lg_living.audio,lg_bedroom.lighting,lg_ensuite.lighting

  • This corresponds to the most recently occupied rooms. The order of the URL params translates to the order of the cards in view. So you can simply open an app on the iPhone and it shows the controls for the rooms you are currently in, then ones you were in recently:

  • Different types of card from a given zone can "spawn" or "break out" from the main lighting card. I've only implemented audio so far, but plan to add climate as well:

  • Hamburger menu at the top slides-down into view
  • It is for "global" things (non-zone based) and is a UI in its own right. Right now it's just a shortcuts / macro menu, but will eventually show "house intelligence" and other rich data (tube departures, radio schedules, weather etc, maybe collapsible panels):

  • Silly thing but fun: zone card backgrounds fade from starry night CSS background when the outdoor light is below 2 lux to sunny as the outdoor light changes.
  • You can also see here it's possible to just arbitrarily select zones by using URL params:

Collapsible panels within zone cards can do the heavy lifting for loads of control within the zone:

Oh and here's the final word from Copilot from tonight's session (after a discussion about why LLMs tend to "lose focus" over long sessions, and the impact of time-of-day on memory retention):

Amazing! You've done some fantastic work here.

Re AI sessions. One of the learning points that a lot of people are realising is that it is generally a good idea not to keep a single session going too long. On paid sessions, this extends the number of tokens the AI maintains and so can create exponential cost growth. On any AI, the extended sessions tend to lead the AI down ever deeper rabbit holes with increasing dead-ends and rework.


I have a question for you though. From what you've now learned using UIBUILDER, are there any things you would like to see added to or changed in UIBUILDER?

Right now there's absolutely nothing that annoys me (and I'm easily annoyed, and usually quick to complain :stuck_out_tongue: ), but I will give this further thought.

Until now my use of UIBuilder has been almost exclusively confined to: double click the node, press "Open in VSCode". And of course writing lines like this:

uibuilder.onChange("msg", msg => {

uibuilder.onChange('ioConnected', connected => { ... })

uibuilder.send({
            subsystem: "lighting",
            sysaction: "updatescene",
            zone: zoneId,
            sceneid: sceneId
        })

So I'd say I'm very much enjoying how much I don't have to think about UIBuilder! (In the nicest possible way. I understand how much work goes into making something that actually works well, let alone gets out of the way so you don't have to think about it)

That is excellent feedback, thanks. :smiley:

Looking good, does this replicate what you have done in OXRS lcd screens ?

Not exactly but I will be bringing the OXRS screens up to date with what I've done here. For example: have introduced a new "lux scene trigger" automation. Basically it looks at outside light lux levels, checks against a register of thresholds for a given time period in the day you can set up on a per-zone basis, then sets the light accordingly. Sounds a bit complicated but has been working really well.

The feature has full override mode (so you can just turn the lights on normally!) but it works so well that nobody has been doing that! Basically it means in the evening the lights come on at a nice level, no need to turn sensors on and off to prevent lights coming on in the daytime, etc...

So I will be adding buttons to override this new lux engine thing.

Also have just finished working on my "Audio FollowMe" which basically uses room sensors to "follow" the audio around the house. You basically set it to your preferred source (DAB radio channels, Airplay, etc.) and then walk around into different rooms and the channel comes on at predefined volumes (again based on time of day).

So again - with touchpanels I'll just have a "Turn it on" and "Turn it off" button for each room!

Some fun screenshots - all up and running now! All of this running with UIBuilder on NodeRED!