NxPanel Installation
To install, firstly flash tasmota as described here;
This will allow you to access the stock screen firmware only
Update: Install has been made much easier
Instead of using the nspanel.be
described above. Use the nxpanel.be
below;
Once you’ve done that, simple type this into the tasmota console
InstallNxPanel
This will download the current version, flash it, then reboot the device in NxPanel
NxPanel will update to the current version automatically without you having to flash anything, if you enable this setting in the configuration page.
You can install it manually if you prefer;
FlashNextion http://<your-openhab-server>:8080/blah/nxpanel.tft
Going back to Stock Screen firmware
You can easily revert back to stock HMI firmware at any time, if you want. Simply use the same process as above. You just need to use the original TFT file. A link to that is here;
Original Stock HMI Firmware
This will revert the Nextion HMI to stock, not the Tasmota side. This process would involve re-flashing the backup taking during the flash of Tasmota. This is not in scope here.
How is works
Before reading on how to configure openhab to work with NxPanel, you might find it useful to jump to this post for a short explanation of how it works. As the following rules are the things to make it happend.
Configuration in OpenHab
You’ll need a channel trigger for the MQTT thing;
- id: nxpanel_command
channelTypeUID: mqtt:string
label: NxPanel Command
description: ""
configuration:
commandTopic: cmnd/nspanel/nxpanel
- id: nxpanel_page_trigger
channelTypeUID: mqtt:trigger
label: NxPanel Page Trigger
description: ""
configuration:
stateTopic: tele/nspanel/RESULT
And you’ll need a rule something like this configure your screens;
This is a groovy
script. Ensure you have install the Groovy Script binding and choose Groovy
when you create the rule. This is a template for you to start with. It works with a couple of components on my system and should give you an idea what you need to do for yours.
Note: There is also a javascript
version of the following rule here which many might find easier.
import org.slf4j.LoggerFactory
def PAGE_HOME = 1
def PAGE_2_BUTTON = 2
def PAGE_3_BUTTON = 3
def PAGE_4_BUTTON = 4
def PAGE_6_BUTTON = 5
def PAGE_8_BUTTON = 6
def PAGE_DIMMER = 7
def PAGE_DIMMER_COLOR = 8
def PAGE_THERMOSTAT = 9
def PAGE_ALERT_1 = 10
def PAGE_ALERT_2 = 11
def PAGE_ALARM = 12
def PAGE_MEDIA = 13
def PAGE_PLAYLIST = 14
def PAGE_STATUS = 15
def BUTTON_UNUSED = 0
def BUTTON_TOGGLE = 1
def BUTTON_PUSH = 2
def BUTTON_DIMMER = 3
def BUTTON_DIMMER_COLOR = 4
def BUTTON_PAGE = 10
def ICON_BLANK = 0
def ICON_BULB = 1
def ICON_DIMMER = 2
def ICON_DIMMER_COLOR = 3
def ICON_VACUUM = 4
def ICON_BED = 5
def ICON_HOUSE = 6
def ICON_SOFA = 7
def ICON_BELL = 8
def ICON_HEAT = 9
def ICON_CURTAINS = 10
def ICON_MUSIC = 11
def ICON_BINARY = 12
def ICON_FAN = 13
def ICON_SWITCH = 14
def ICON_TALK = 15
def ICON_INFO = 16
def NONE = 0
def logger = LoggerFactory.getLogger("org.openhab.core.automation.nspanel")
def mqtt = actions.get("mqtt","mqtt:broker:mqtt_broker")
def str = event.getEvent()
logger.info("Demo page rules called")
if (str.indexOf('{"page":')!=0) {
return
}
/*
* Utility functions - start
*/
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) {
var str = new StringBuilder('{"refresh":')
str<<'{"pid":'<<pid<<',"name":"'<<name<<'",'
return str
}
def makeEmptySync(pid) {
var str = new StringBuilder('{"sync":')
str<<'{"pid":'<<pid<<'}}'
return str
}
def makeEmptyRefresh(pid) {
var str = new StringBuilder('{"refresh":')
str<<'{"pid":'<<pid<<'}}'
return str
}
def makeSyncButtonStart(pid,bid,state) {
var str = new StringBuilder('{"sync":')
str<<'{"pid":'<<pid
str<<',buttons:[{"bid":'<<bid<<',"state":'<<state<<'}'
return str
}
def addSyncButton(bid,state) {
var str = ',{"bid":'<<bid<<',"state":'<<state<<'}'
return str
}
/*
* Utility functions - end
*/
/*
* 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
def PANEL_MAIN = 10
def PANEL_BEDROOM_1 = 11
def PANEL_BEDROOM_2 = 12
def PANEL_LOUNGE = 13
def PANEL_CABIN = 14
def PANEL_CABIN_THERMO = 15
def PANEL_CABIN_LIGHTS = 16
def PANEL_LOUNGE_FAN = 17
def PANEL_LOUNGE_LIGHT = 18
def PANEL_STATUS = 19
def PANEL_MUSIC = 20
def TOPIC = "cmnd/nspanel/nxpanel"
logger.info("updating page ... "+id)
switch (id as int) {
case PANEL_MAIN :
logger.info("main panel")
// set these from your own items
movie_state = 1
lounge_state = 0
cabin_state = 0
hall_light_state = 1
if (refresh) {
json = makePage(id,'Lounge')
json<<format<<'buttons:['
json<<makeButton(1,"Movie",BUTTON_TOGGLE,ICON_BULB,movie_state)
json<<makeButton(2,"Lounge",BUTTON_TOGGLE,ICON_BULB,lounge_state)
json<<makeButton(3,"Hall",BUTTON_PUSH,ICON_HOUSE)
json<<makeButton(4,"Bedroom",BUTTON_PAGE,ICON_BED,PAGE_6_BUTTON,PANEL_BEDROOM_1)
json<<makeButton(5,"Temp",BUTTON_PAGE,ICON_HEAT,PAGE_THERMOSTAT,PANEL_CABIN_THERMO)
json<<makeButton(6,"Light",BUTTON_DIMMER,ICON_DIMMER,hall_light_state,PANEL_LOUNGE_LIGHT)
json<<makeButton(7,"Dimmer",BUTTON_DIMMER_COLOR,ICON_DIMMER_COLOR,cabin_state,PANEL_CABIN_LIGHTS)
json<<makeButton(8,"Status",BUTTON_PAGE,ICON_INFO,PAGE_STATUS,PANEL_STATUS)
json<<"]}}"
} else {
json = makeSyncButtonStart(id,1,movie_state)
json<<addSyncButton(2,lounge_state)
json<<"]}}"
}
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_BEDROOM_1 :
// set these from your own items
fan_state = 1
if (refresh) {
json = makePage(id,'Bedroom 1')
json<<format<<'buttons:['
json<<makeButton(1,"A",BUTTON_PUSH,ICON_HOUSE)
json<<makeButton(2,"Fan",BUTTON_DIMMER,ICON_FAN,fan_state,PANEL_LOUNGE_FAN)
json<<makeButton(3,"C",BUTTON_PUSH,ICON_SOFA)
json<<makeButton(4,"Music",BUTTON_PAGE,ICON_MUSIC,PAGE_MEDIA,PANEL_MUSIC)
json<<makeButton(5,"D",BUTTON_PUSH,ICON_TALK)
json<<makeButton(6,"Alarm",BUTTON_PAGE,ICON_BELL,PAGE_ALARM,NONE)
json<<"]}}"
} else {
json = makeEmptySync(id)
}
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_BEDROOM_2 :
json = makeEmptySync(id)
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_LOUNGE :
json = makeEmptySync(id)
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_CABIN :
json = makePage(id,'Cabin')
json<<"}}"
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_CABIN_THERMO :
// set these from your own items
var heater = 1
var auto = 0
var temp = 15
var set = 21
json = makePage(id,'Cabin')
json<<format<<',"therm":{'
json<<'"set":'<<set<<',"temp":'<<temp<<',"heat":'<<heater<<',"state":'<<auto<<'"'
json<<"}}"
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_CABIN_LIGHTS :
json = makePage(id,'Cabin Lights')
json<<'"power":'<<ON<<',"hsbcolor":'<<'"10,100,50"'
json<<"}}"
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_LOUNGE_FAN :
// set these from your own items
fan_state = ON
fan_setting = 3
json = makePage(id,'Lounge Fan')
json<<'"power":'<<fan_state<<',"min":'<<1<<',"max":'<<4<<',"icon":'<<ICON_FAN<<',"dimmer":'<<fan_setting
json<<"}}"
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_LOUNGE_LIGHT :
json = makePage(id,'Lounge Light')
json<<'"power":'<<ON<<',"dimmer":'<<30
json<<"}}"
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_STATUS :
json = makePage(id,'System Status')
json<<'"status":['
json<<'{"id":'<<1<<',"text":'<<'"Gate":'<<',"value":'<<'"Open"'<<',"color":'<<2<<'}'
json<<','
json<<'{"id":'<<2<<',"text":'<<'"Window":'<<',"value":'<<',"Shut"'<<',"color":'<<3<<'}'
json<<','
json<<'{"id":'<<5<<',"text":'<<'"Room Temp":'<<',"value":'<<',"20°C"'<<'}'
json<<']}}'
mqtt.publishMQTT(TOPIC, json.toString())
break
case PANEL_MUSIC :
json = makePage(id,'Sonos Player')
// set these from your own items
json<<'"artist":'<<'"New Order"'<<',"album":'<<'"Movement"'<<',"track":'<<'"Power Play"'<<',"volume":'<<70
json<<"}}"
mqtt.publishMQTT(TOPIC, json.toString())
break
default :
logger.info("unknown page!")
break
}
logger.info("rule done")
The Process
As NxPanel moves to a new screen, it will message over MQTT the page id to identify what room it’s looking for to render the page. If the screen was last used to show that room, it messages it only needs a sync of the room. the OH rule then only needs to send back a lightweight message with the states of buttons and so on. If the page was not showing this room last time, the message sent to OH will be a refresh one, and OH will send a heavyweight message back which will tell NxPanel how to render all the buttons and label and room titles.
Only the Home screen and Button pages will remember the last room, other screens will call refresh each time. This is purely down to memory of the Nextion. It only has 3K, which is not enough to remember that state of each screen page.
Useful Things
The buzzer on the NSPanel doesn’t seem to be set to work by default. I needed to do the following in Tasmota. The Alarm panel used this as do some other places.
buzzerpwm 1
UK Timezones
TimeZone 99
TimeDST 0,0,3,1,1,60
TimeSTD 0,0,10,1,2,0