NSPanel Lovelace UI Helpers (part 1/5, v0.9)

See JSR223 Scripting | openHAB

You’d use the JS example:


var valueX = sharedCache.get("x")

It is not unusual to only support the current release forward when developing new features, add-ons, libraries, etc. The only reason I have any Rule Templates that support OH 3 is OH 3 was the version released when I wrote them and I used Nashorn JS for it. I completely rewrote them from scratch for OH 4 using the newer GraalVM JS and added a dependency on my openhab-rules-tools library (most of my templates have a ton of shared code and I didn’t want to maintain the same code across lots of templates, thankfully openhab-rules-tools can be installed from openHABian).

I pretty much only minimally support the OH 3 templates now.

Hi Rich, thanks for this explanation, seems like you can really do everything with OH3 javascript. As of now I will stay with OH4 compatibility, if there are more requests coming in for OH3 I might implement another OH3 version at the point the current software seems more stable.

Best regards and a nice end of the year, Rene


just released version 0.4. Happy new Year!

Be aware that this version is not backward compatible with 0.3 and your blockly-scripts might not work anymore. I did the upgrade on my own machine by removing all the complex items I added to the NSPanel cards or screensavers (just moved them to the side in blockly and saved the scripts). Then did the upgrade, opened all the scripts and reattached the items to the newly retrieved cards I got from the menus. If you don’t do this before installing the new version you might loose your blockly scripts completely.

I hope that this is he last time I have to do such a major change. This change was required to use the scripts which you use to build you pages (cards, screensaver-updates) on multiple different hardware panels. The information about the NSPanel Item is now only hold in your Callback, therefore you need only one callback per hardware.

Sorry for the trouble and I hope you are going to enjoy the new version. As said, happy new year!

Hi, just released the bugfix version 0.5.

It will fix the broken refresh timers of 0.4. Just read the post above about the release of 0.4 if you are planning to upgrade from 0.3. If you are upgrading from 0.4 there will be no problem.

All the best and sorry for the short update-cycle, Rene


Since version 0.4 you can let the button channels also trigger the callback as a result of any changes in the button channels. Just create these channels monitoring the mqtt topics of the buttons (sorry, don’t have them at hand just now). Add them to your CallBack and it will forward a “refresh” to the page (Card)-script you had displayed on your panel. There you can read the current button state and do some action, if you like.

Technically, if any Channel which is monitored by the CallBack rule for updates changes into ‘refresh’, ‘ON’ or ‘OFF’ this refresh trigger will be send to the previously displayed card. If the Screensaver is active, this only works when you remember the recent Page to restart at the same position when leaving the ScreenSaver (run also after leaving screensaver unchecked). In the other case (having some landing-page) you might be able to react on this change with some additional rule triggering some action the usual way.

Hope this helps, if you have any further questions, requests or ideas feel free to come back,

best regards, Rene

Thanks for the explanation. When I press the buttons I do not want the screen to do anything. I just ended up creating a couple of additional channels and having my own rules deal with the mqtt messages.

Now to another problem.

On the callback script I checked the use complex screensaver, and on the startup section I clicked the run also after leaving screensaver and added the entity script name

I also created a rule that triggers every time that any of the entities (items) in the complex screen saver changes value and runs the complex screensaver script.

Now, from the screensaver I can go to the entities screen by touching the screen until one of the items in the complex screensaver gets updated, after that the screen stops responding to touch and just remains on the screensaver.

This is the log after I run the screensaver update

2024-01-07 19:19:28.969 [INFO ] [omation.script.ui.nspanel_k_callback] - waiting 1 sec. for running rule update_complex_ss
2024-01-07 19:19:28.969 [INFO ] [tomation.script.ui.update_complex_ss] - starting script 'absorb_it_nspanel_weatherupdate' {event=Execution triggered by manual, triggeredLoadPage=1, ruleUID=update_complex_ss}
2024-01-07 19:19:29.970 [INFO ] [tomation.script.ui.update_complex_ss] - starting script 'absorb_it_nspanel_notify' {timerName=absorb_it_nspanel_refreshTimer_nspanel_k_callback, ruleUID=update_complex_ss, target=NSPanel_K_Lovelace_UI}
2024-01-07 19:19:29.973 [INFO ] [tomation.script.ui.update_complex_ss] - starting script 'absorb_it_nspanel_weatherupdate' {timerName=absorb_it_nspanel_refreshTimer_nspanel_k_callback, ruleUID=update_complex_ss, target=NSPanel_K_Lovelace_UI}
2024-01-07 19:19:29.981 [INFO ] [tomation.script.ui.update_complex_ss] - starting script 'absorb_it_nspanel_notify' {request=pageUpdate, item=null, previousPage=update_complex_ss, timerName=absorb_it_nspanel_refreshTimer_nspanel_k_callback, ruleUID=update_complex_ss, trigger=null, newState=null, target=NSPanel_K_Lovelace_UI}
2024-01-07 19:19:29.985 [INFO ] [tomation.script.ui.update_complex_ss] - starting script 'absorb_it_nspanel_weatherupdate' {request=pageUpdate, item=null, previousPage=update_complex_ss, timerName=absorb_it_nspanel_refreshTimer_nspanel_k_callback, ruleUID=update_complex_ss, trigger=null, newState=null, target=NSPanel_K_Lovelace_UI}

If I use a change in the items in the complex screensaver as triggers for the callback rule the items don’t get updated until I touch the screen and after the timeout the screen returns to the screensaver.

Not sure what I am missing.


Hi, I assume that the problem lies in the way you are starting the updates of the screensaver items.

You can move the configuration script for your screensaver into the script section and start this script with a rule and the Start Script With Context helper, this should work as of now.

The setNSPanelItemIfNotSet helper seems to be broken for your use-case. The callback will store the last triggered page to be opened if you leave the screensaver - and with the setNSPanelItemIfNotSet you will change this ‘last page’ to some screensaver configuration, not a page. Probably the “NSPanel Item if not set” implementation needs to be changed, I will check this asap.

Thanks for your feedback - best regards,

That fixed the problem.
Now it is working great

thank you

Hi I have a question on the value returned on the shutter entity.

On the blind THING I have two channels with the associated items, a rollershutter and a dimmer
In the shutter entity I read the dimmer value to set the slider and I use the rollershutter item for the return value

This does not work for either up, down, stop or the slider. By not work I mean that the blind does not move

If I use a string item to accept the return value I can then use a rule to convert the up stop down into UP STOP DOWN commands for the rollershutter item. The rule is for a different blind but the results are the same.

var String IsNum = null

 try {
        val int num = Integer.parseInt(MBBlindControl.state.toString)
    } catch (NumberFormatException e) {
IsNum = "no"        // String is not a number
logInfo("IsNum ", IsNum)
if(IsNum == "no"){
if(MBBlindControl.state == "up") gMBRoller.sendCommand(UP)
if(MBBlindControl.state == "down") gMBRoller.sendCommand(DOWN)
if(MBBlindControl.state == "stop") gMBRoller.sendCommand(STOP)}

I still cannot convert the string into a number to pass it on to the rollershutter item. In the rule above IsNum is always “no” even though I can see the string item being something like 50.0 or XX.0 in the general case.

Should having a rollershutter type item to receive the ReturnValue work?

Sorry for all the questions


Hi clark,

you are right, the blockly part will not work directly. The resulting values (returnValue) are just as they come from the NSPanel Lovelace UI implementation, I was not aware that they are so close to the required values to control a rollershutter item.

You can get the required values in blockly as this:

(btw, the description of the tilt area is wrong, it will give you ‘open’, ‘stop’ and ‘close’ as returnValue)

This seems to be invalid and always triggers some error - best avoid try/catch statements in javascript to see this. You can use the following code for transformations, if you like to do them in javascript (not tested, just written down):

switch (MBBlindControl.state) {
  case "up":
  case "down":
  case "stop":

I will cleanup the shutter entity and let it return some suitable values in the next release. Thanks for testing and pointing to this issue. Best regards,

Hi again

Neither of those approaches works for me, probably because I really don’t understand Java so I just try to adjust the examples I see.

For the blockly side this is the code, which I think matches what is in your reply

This is the log when I run it

8:52:34.340 [INFO ] [nhab.automation.script.ui.Entities_MB] - trigger shutter actionSTOP
08:52:34.345 [WARN ] [ernal.defaultscope.ScriptBusEventImpl] - Command 'stop' cannot be parsed for item 'Studio_Blind_2_Rollershutter (Type=RollershutterItem, State=55.0, Label=Studio Blind 2 Rollershutter, Category=blinds, Tags=[Point])'.

It looks like the string “stop” is still being sent to the command

On the javascript side I get this error

MBBlindControl not defined
I am somewhat used to DSL, I really don’t know javascript so I don’t know what has to be defined before the code accepts it.


After a lot of trial and error I got the rule to control the blinds both the commands and the blind position to work

var String IsNum = "yes"

 try {
        val Number num = Float::parseFloat(Kitchen_Blind_Control.state.toString)
    } catch (NumberFormatException e) {
IsNum = "no"        // String is not a number

if(IsNum == "no"){
  if(Kitchen_Blind_Control.state == "up") Broadlink_RM4_Command.sendCommand("KBlind_Up")
  if(Kitchen_Blind_Control.state == "down") Broadlink_RM4_Command.sendCommand("KBlind_Down")
  if(Kitchen_Blind_Control.state == "stop") Broadlink_RM4_Command.sendCommand("KBlind_Stop")
  val Number num = Float::parseFloat(Kitchen_Blind_Control.state.toString)

Thanks for the suggestions

@rene_rostock, this library is really great! I’m currently using it to set up an NSPanel and found most of the UI elements that I need already.

It would be nice to have an UI element that allows to change values directly within an entities card. I’ve drawn into the screenshot below how this could look like. It would be nice to use the up and down arrows to directly change the values.
But if I understand the architecture of this library correctly, then it triggers the screens of lovelace UI and as I did not find a functionality like this in lovelace UI, it is probably not possible to have feature like this. Is that correct?

Hi, just released version 0.6

It was mainly done to prevent the issue of using some temporary context to run some screensaver configuration, which was leading to some ‘unresponsive’ screensaver.

There are some changes which might raise incompatibilities. Be aware during your upgrades…

  • enhancement: changed returnValue uppercase for cardShutter to be compatible with openHAB shutter item
    • fix the returnValues of your shutter Cards to use the new version
  • cleanup: moved fullpage Notification to Cards
    • you need to use the new fullPage notification, therefore just remove all configuration Items from the previous card and re-attach them later to the new card you got from the nspanel_cards library

This should be it, but it’s really hard for me to check if there are some other side-effects, be warned as usual :slight_smile:

And enjoy, Rene

Hi isse, thanks for the flowers :slight_smile:

Yes, I can only provide what lovelace UI gives to us. Maybe you can ask there, I don’t know how difficult it is for Johannes joBr99 to implement that.

Best regards, Rene

Thanks for Rene for confirming this. I’ve asked the question again on github.

Currently, I’m using thermo cards to set the values. But as these cards were initially designed to set temperatures, they are of cause not ideal for the task. Again, I’m not sure if this could be done in your script @rene_rostock or if this is part of the lovelace UI.

Would it be possible set different units than °C or F, or even remove the unit? In most cases I’m setting power values in W.

As these values are sometimes greater than 1000 W, there is not enough space for decimal places on the screen. As they are not required for this use case, would it be possible to remove then?

Hi, seems like a famous tradition, but the wrong way. The even version numbers are unstable releases, the odd ones are stable :frowning:

There was some issue with 0.6, which is fixed now in 0.7, sorry for your trouble. Now you got the bugfix and …

you can remove the unit with the 0.7-cardThermo as well. Enjoy!



For the time updater what time and date formats are accepted? I looked at this document

Date/Time Conversions

The following date and time conversion suffix characters are defined for the 't' and 'T' conversions. The types are similar to but not completely identical to those defined by GNU date and POSIX strftime(3c). Additional conversion types are provided to access Java-specific functionality (e.g. 'L' for milliseconds within the second).

The following conversion characters are used for formatting times:

'H' Hour of the day for the 24-hour clock, formatted as two digits with a leading zero as necessary i.e. 00 - 23.
'I' Hour for the 12-hour clock, formatted as two digits with a leading zero as necessary, i.e. 01 - 12.
'k' Hour of the day for the 24-hour clock, i.e. 0 - 23.
'l' Hour for the 12-hour clock, i.e. 1 - 12.
'M' Minute within the hour formatted as two digits with a leading zero as necessary, i.e. 00 - 59.
'S' Seconds within the minute, formatted as two digits with a leading zero as necessary, i.e. 00 - 60 (“60” is a special value required to support leap seconds).
'L' Millisecond within the second formatted as three digits with leading zeros as necessary, i.e. 000 - 999.
'N' Nanosecond within the second, formatted as nine digits with leading zeros as necessary, i.e. 000000000 - 999999999.
'p' Locale-specific morning or afternoon marker in lower case, e.g.“am” or “pm”. Use of the conversion prefix 'T' forces this output to upper case.
'z' RFC 822 style numeric time zone offset from GMT, e.g. -0800. This value will be adjusted as necessary for Daylight Saving Time. For long, Long, and Date the time zone used is the default time zone for this instance of the Java virtual machine.
'Z' A string representing the abbreviation for the time zone. This value will be adjusted as necessary for Daylight Saving Time. For long, Long, and Date the time zone used is the default time zone for this instance of the Java virtual machine. The Formatter’s locale will supersede the locale of the argument (if any).
's' Seconds since the beginning of the epoch starting at 1 January 1970 00:00:00 UTC, i.e. Long.MIN_VALUE/1000 to Long.MAX_VALUE/1000.
'Q' Milliseconds since the beginning of the epoch starting at 1 January 1970 00:00:00 UTC, i.e. Long.MIN_VALUE to Long.MAX_VALUE.

The following conversion characters are used for formatting dates:

'B' Locale-specific full month name, e.g. "January", "February".
'b' Locale-specific abbreviated month name, e.g. "Jan", "Feb".
'h' Same as 'b'.
'A' Locale-specific full name of the day of the week, e.g. "Sunday", "Monday"
'a' Locale-specific short name of the day of the week, e.g. "Sun", "Mon"
'C' Four-digit year divided by 100, formatted as two digits with leading zero as necessary, i.e. 00 - 99
'Y' Year, formatted as at least four digits with leading zeros as necessary, e.g. 0092 equals 92 CE for the Gregorian calendar.
'y' Last two digits of the year, formatted with leading zeros as necessary, i.e. 00 - 99.
'j' Day of year, formatted as three digits with leading zeros as necessary, e.g. 001 - 366 for the Gregorian calendar.
'm' Month, formatted as two digits with leading zeros as necessary, i.e. 01 - 13.
'd' Day of month, formatted as two digits with leading zeros as necessary, i.e. 01 - 31
'e' Day of month, formatted as two digits, i.e. 1 - 31.

I tried something like ll:mm to show the time as a 12 hour clock and it gives an error

2024-01-16 21:59:00.203 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: p: Unknown pattern letter: l

Are the default formats the only options?

Thank you

Hi clark,

Surely not, but honestly I don’t know a list of possible options. I’m just re-using the formatter which is used in blockly anyway…

which gets converted to


most likely using all the options from DateTimeFormatter (Java Platform SE 8 )

Regarding your special case, the required format string will be “hh:mm a”, but the ‘a’ (for AM/PM) will give an error (no idea how to fix that):

Pattern using (localized) text not implemented, use @js-joda/locale plugin!

Personally I did not found any way to get the Day of the Week as a name included, therefore I created the required strings manually like this:

Maybe you can do the same for yourself to add AM/PM?

Hope this helps somehow, best regards,

Thank you for the very detailed response. I’ll work on it based on your example.