NxPanel - Replacement Firmware for Sonoff NSPanel

@m-home I’m really impressed with your work, it seems to work nicely. I’ve managed to get the weather and notification working. Screen dimming fixed with the latest version.

I guess you can build a generic rule that picks up button presses and items using a map file or similar. Beyond my skill level but surely there must be a way to avoid creating lots of channels and items?

I’m sure the basic on/off switches can be done by a generic rule, where the item can just be added to a group. you’d just need to set the pid an bid on the item, maybe in metadata, but I’v not used that before.

when I can load YAML config files in groovy, it should be even easier as you will not need to edit a rule, there will just be a generic rule and loads the config file.

the config file will basically say the pid (Screen/Page ID) & bid (Button on Page) maps to this OH Item.

But current, I don’t have that. But the current way works and delivers the same end point, just more work getting there.

v1.0.0-beta4

This release has the ability to set button state via MQTT.

You should set the state in the main page rule for managing pages, in both refresh and sync messages, sent from the panel.

If you want to keep it in real-time sync, your item should also send a sync message for the single button it relates to. For buttons, only toggle ones need a sync, the rest only need an entry in the main refresh event to create them.

The sync message for buttons is of this format;

{"sync": "pid":10, buttons:[{"bid":6, "state":0}]}

For testing, it’s handy to use the linux mosquitto_pub command, such as;

mosquitto_pub -t "cmnd/nspanel/nxpanel" -m '{"sync": "pid":10, buttons:[{"bid":6, "state":0}]}'

Setting pid is important. or your message may update buttons on the wrong page!! :slight_smile:

For the thermostat you are looking at this;

{"refresh":{"pid":12,"name":"Cabin","format":9,"therm":{"set":17,"temp":1,"heat":0,"state":0"}}

There is not sync as you move to this page as it has no ‘kept state’. It will always request refresh. You can however send a sync to it, if you want real-time updates, while it’s the current shown page.

The same will apply for any other kind of page, other than buttons and home. As there was only enough memory to keep state on these.

Hope that helps and answers the questions from early today.

I’ve some tidy up todo around the dimmer controls and them I think that will be a version 1 release unless anyone has found anything else. I’ll also make the a button of type thermostat behave like a dimmer, single press on/off , longer press, jumps to the control itself.

Im having another play this afternoon - can someone explain how they are capturing the MQTT messages from button presses and linking these to items. I’ve been testing and it seems that the groovy script which is triggered on page_trigger has this code

def str = event.getEvent()
if (str.indexOf('{"page":')!=0) {
  logger.info("ignore: "+str)
  return

So if just try and parse the tele/NSPanel/RESULT I get this in my log file.

2022-02-07 17:51:20.099 [INFO ] [org.openhab.core.automation.nspanel ] - ignore: {"button": {"pid": 15, "bid": 1, "state": 1, "next": 0}}

Try this;

Create a new rule;

import org.slf4j.LoggerFactory

def logger = LoggerFactory.getLogger("org.openhab.core.automation.nspanel")

def str = event.getEvent()
if (str.indexOf('{"button": ')!=0) {
  return
}

// {"button": {"pid": 15, "bid": 1, "state": 1, "next": 0}}

var i = str.indexOf("\"pid\"")
var i2 = str.indexOf(",",i+7)
var pid = str.substring(i+7,i2) as int
i = str.indexOf("\"bid\"")
i2 = str.indexOf(",",i+7)
var bid = str.substring(i+7,i2) as int
i = str.indexOf("\"state\"")
i2 = str.indexOf(",",i+9)
var state = str.substring(i+9,i2) as int

if (pid==10 && bid==1) {
  events.sendCommand("movie_room_lights",state==1?"ON":"OFF")
}
else if (pid==10 && bid==2) {
  //
}

trigger it the same as the others.

Remove the line in the other rule that print the “ignore…” message that spams the log.

Have you been able to test that - I’m still hitting a brick wall with the fact that I can’t get at the value from the json. It publishes it to the database but somehow rules are not triggering. For example I setup a simple number Item called NXPanel which gets the bid from a channel. The Item updates fine but the rules are not firing.

rule "Test Button Presses"
when
    Item NXPanel changed
    then
        if (NXPanel.state == 1) {
        Lt_Office_Ceiling.sendCommand(OFF)
        logInfo("nspanelrule","Button 1 pressed ")
    }
        if (NXPanel.state == 2) {
        Lt_Office_Ceiling.sendCommand(ON)
        logInfo("nspanelrule","Button 2 pressed ")
    }
    else
        logInfo("nspanelrule","Nothing pressed ")
end

See above for rule solution.

Great works perfectly! Just had to change the pid to 15 on the if statement.

I guess there must be a bug in OH3 that does not allow groovy and regular rules to fire off the same trigger.

Thanks again for the help

I think you can do both fine.

Your rule was;

when
    Item NXPanel changed
    then

But I think it should be a Channel trigger, not a Item one.
Something like this;

when
    Channel "astro:sun:home:rise#event" triggered START
then

But using this part;

But it’s not my area of knowledge. But I think all the UI and File based, should all work, and you can mix and match.

v1.0.0-beta5

Some small fixes around sync refreshing

IMPORTANT To keep things in order I renamed the id field that is sent to the rule to be pid as below;


var i = str.indexOf("\"pid\"")
var i2 = str.indexOf(",",i+7)
var id = str.substring(i+7,i2)
i = str.indexOf("\"format\"")
i2 = str.indexOf(",",i+10)
var format = str.substring(i+10,i2)

You’ll need to change that, and the 6 to 7 in the substring as it’s a char longer now.

The spare space on home screen. I was thinking of a fast access button. You assign it to jump directly form home screen to preferred page. It could be the right/swipe screen (without having to swipe), but you could assign it to any other preferred screen.

Good/Bad idea?

4 Likes

Good idea.
Personnally I don´t like swiping, if it´s not super smooth.

It’s in beta6

image

{“favorite”: {“pid”: n, “format”: n}}

Yes that would be useful - although I would prefer to be able to choose the icon so for example lights or music etc…

I’ve got sync working so getting there slowly. Can you explain the requirements for Music player and playlist setup.
Thanks

Seeing a strange issue, buttons on 3 button page toggling the wrong button, select button 2 and button 3 turns on/off, select button 3 turns button 3 on/off. Button 1 works as expected. See behaviour after power cycle. On v1.0.0-beta1 of nxpanel.be and beta6 of the TFT. MQTT shows the following after power cycle when I swipe to page 10, select the first button and then tap button1, button2 and button3.

{“page”: {“format”: 6, “pid”: 10, “type”: “refresh”}}
{“refresh”:{“pid”:10,“name”:“Family Room”,“format”:6,buttons:[6,buttons:[{“bid”:1,“label”:“Heat”,“type”:10,“next”:11,“state”:3,“icon”:9}]}}
{“NxPanel”:“Done”}
{“page”: {“format”: 3, “pid”: 11, “type”: “refresh”}}
{“refresh”:{“pid”:11,“name”:“Hot Water”,“format”:3,buttons:[{“bid”:1,“label”:“1 Hour”,“type”:1,“icon”:14},{“bid”:2,“label”:“1.5 Hours”,“type”:1,“icon”:14},{“bid”:3,“label”:“2 Hours”,“type”:1,“icon”:14}]}}
{“NxPanel”:“Done”}
{“button”: {“pid”: 11, “bid”: 1, “state”: 1, “next”: 0}} UU
{“button”: {“pid”: 11, “bid”: 3, “state”: 1, “next”: 0}} UU
{“button”: {“pid”: 11, “bid”: 3, “state”: 0, “next”: 0}} UU

Script for page refresh attached.

import org.slf4j.LoggerFactory

def logger = LoggerFactory.getLogger("org.openhab.core.automation.nspanel")

/*
 * YOU ONLY NEED TO EDIT THE SWICH PART BELOW
 */

/*
 * Utility functions
 */

def makeButton(bid,label,type,icon=null,state=null,next=null) {
  var str = ""<<((bid==1)?"":",")
  str<<'{"bid":'<<bid<<',"label":"'<<label<<'","type":'<<type
  if (next!=null) {
    str<<',"next":'<<next
  }
  if (state!=null) {
    str<<',"state":'<<state
  }
  if (icon!=null) {
    str<<',"icon":'<<icon
  }
  str<<'}'
  return str
}

def makePage(pid,name,format) {
  var str = new StringBuilder('{"refresh":')
  str<<'{"pid":'<<pid<<',"name":"'<<name<<'","format":'<<format<<',buttons:['
  return str
}

def str = event.getEvent()
if (str.indexOf('{"page":')!=0) {
  logger.info("ignore: "+str)
  return
}

/*
 * Get data from the page message
 * (would be good to use JsonSluper here but currently can't access)
 */

var i = str.indexOf("\"pid\"")
var i2 = str.indexOf(",",i+7)
var id = str.substring(i+7,i2)
i = str.indexOf("\"format\"")
i2 = str.indexOf(",",i+10)
var format = str.substring(i+10,i2)

// check if a full refresh or just a status update
var refresh = str.indexOf("refresh")>0

var json

switch (id) {
  
  /*
   * MakePage has these params;
   *  pid - you unique id for the page (max 99)
   *  name - 25 chars of description
   *  format - The style of page (will be set from what calls it, but this is to validate)
   *   1  - home
   *   2  - 2 buttons
   *   3  - 3 buttons
   *   4  - 4 buttons
   *   5  - 6 buttons
   *   6  - 8 buttons
   *   7  - dimmer Brightness
   *   8  - dimmer Color
   *   9  - Thermostat
   *   10 - Alert 1
   *   11 - Alert 2
   *   12 - Alarm
   *   13 - Media Player
   *   14 - Media Playlist
   *   15 - Status Panel
   *
   * 10 is the root button page from the home screen
   * it's set to be an 8 button panel for now, but you'll be able to change
   *   
   * MakeButton has these params;
   *  bid - number of button (1-8), keep in correct order, can leave blank
   *  label - the label under the button (8 chars max)
   *  type:
   *   0  - Unused
   *   1  - Toggle
   *   2  - Push
   *   3  - Dimmer (press on/off, long press for advanced)
   *   4  - Dimmer RGB (press on/off, long press for advanced)
   *   10 - Page Link
   *  icon: (can add more on request!)
   *   0  - Blank
   *   1  - Bulb
   *   2  - Dimmer 1
   *   3  - Dimmer 2
   *   4  - Vaccum
   *   5  - Bed
   *   6  - House
   *   7  - Sofa
   *   8  - Bell
   *   9  - Heat
   *   10 - Curtains
   *   11 - Music
   *   12 - Binary
   *   13 - Fan
   *   14 - Switch
   *   15 - Talk
   *  state - button 0=off, 1=on (for toggles), or page format for links (type 10)
   *  next  - id of next page from the section below
   *    
   */

  /*
   * EDIT BELOW HERE TO SET UP YOUR ROOMS
   * 10  is the default start page, when your right swipe the home screen.
   */
  
// ensure you have a sync and a refresh for each, even if sync is empty.

  case "10" :
    movie = ir.getItem("MagicHome_1").state==ON?1:0
      json = makePage(id,'Family Room',format)
      json<<format<<',buttons:['
      json<<makeButton(1,"Heat",10,9,3,11)
      json<<"]}}"
  
    events.sendCommand("nxpanel_command",json.toString())
    break
  case "11" :
//    if (refresh) {
      json = makePage(id,'Hot Water',format)
      json<<makeButton(1,"1 Hour",1,14)
      json<<makeButton(2,"1.5 Hours",1,14)
      json<<makeButton(3,"2 Hours",1,14)
      json<<"]}}"
//    } else {
//      json = '{ "sync": {} }'
//    }
    events.sendCommand("nxpanel_command",json.toString())
    break
}

There looks to be a few issues with the script -
The makePage needs an Id number and Format number
You are calling movie which does not tie in with the button
You have also commented out some of the code

Here is my code for a single page setup (I use a separate rule for commands)


  case "10" :
      if (refresh) {
      json = makePage(10,'Master Bedroom',6)
      json<<format<<',buttons:['
      json<<makeButton(1,"Walls",1,1)
      json<<makeButton(2,"Bath",1,1)
      json<<makeButton(3,"Blinds",1,10)
      json<<"]}}"
    } else {
      json = '{ "sync": {} }'
    }
    events.sendCommand("nxpanel_command",json.toString())
    logger.info("nxpanel_command: "+json)
    break

I’ve added a log at the bottom so you can check what is being sent to the panel

thanks for the detailed info. I’ll add a 3 button like yours on mine later and see if i can see what is going on.

I don’t; know anything about your music backend. I don’t have one, but I’m guessing you have an OH item which says what is playing and a command channel too, to send things to make it next and so on?

So the principal would be to have a trigger on that item changing. get the album/track/arist and so on, then send a {“media”:{“artist”:“blah”,“track”:“blah”…}} message to the panel

probably best to get to grips with that first, then do playlist.

Yes, I see the same issue. Very odd!? I’ll have the pleasure of trying to debug that on the nextion later!! lol great fun! :slight_smile: