NSPanel with custom GUI and synchronized button states

The goal of this project was to provide a level of redundancy for family members who do not rely upon the Home Assistant Companion app and use a tablet for common tasks. I have considered this tablet a single point of failure and have patiently waited for my Sonoff NSPanel delivery. Which came in a timely 5 days after this video was released providing a huge benefit in getting started.

Basing my efforts on ESPHome and the basic concepts provide by @masto, I have further customized the Nextion GUI and ESPHome yaml with two goals in mind;

  1. match the 3x3 grid layout of the Lovelace dashboard on the tablet
  2. synchronize on/off state using the same mechanism of changing icon background
  3. use the second button for something…

Without being a complete walkthrough I will outline a few key challenges and points.

GUI Design
In order to meet objective #1, I downloaded the same MDI icons deployed in the Lovelace grid and matched the fore/background colors. These are then uploaded to the Nextion Editor and deployed a dual_state buttons using pic and pic2 attributes.

I also went ahead and added a bezel to the icons in order to get around the lack of rounded corners/transparency support without a great deal of effort as no one in the household will pay that close attention to it.

Ingest HA Entity State into ESPHome
In order to have a Nextion object change its “icon” (pic object) to match the state of an HA entity, you need to pull that entity into ESPHome. This is fairly straightforward and well documented.

  - platform: homeassistant
    id: movie_lighting
    entity_id: light.movie_lighting

The single biggest challenge I faced was to figure out how to change the Nextion value to match the imported state. I could not find the answer in the ESPHome docs and ended up scouring through code which I do not understand in order to test a dozen plausible functions/methods.

The following is what I ended up with and works great.

    on_state:
       then:
          - lambda: |- 
                  if (id(movie_lighting).state) {
                    id(disp1).set_component_value("movie",1);
                  } else {
                    id(disp1).set_component_value("movie",0);
                  }
          - component.update: disp1

When the HA state changes, this triggers ESPHome to update the display component value to either 0 or 1. These correspond to the pic and pic2 you uploaded in the Nextion Editor.

Now that we have state reflecting correctly, we want the button do actually do something.

Button Logic
I provided objname to all my button objects in the Nextion Editor. And instead of relying upon HA automation to trigger an event, I created the desired automation in ESPHome for faster execution.

  - platform: nextion
    name: $device_name Movie
    id: movie
    page_id: 0
    component_id: 8
    on_press:
      then:
        if:
          condition:
            lambda: 'return id(movie_lighting).state;'
          then:
            - homeassistant.service:
                service: light.turn_off
                data:
                  entity_id: light.movie_lighting
            - component.update: disp1
          else:
            - homeassistant.service:
                service: light.turn_on
                data:
                  entity_id: light.movie_lighting
            - component.update: disp1  

Now when you press the Movie button, it turns on a HA Template Light for me.

With that done, objective #2 is not quite complete since any NSPanel reboot or firmware update will revert all buttons to the Nextion pic object even if that does not reflect the state of the HA entity.

Getting ESPHome to synchronize state on boot was the second greatest challenge and a few more hours of testing in order to come up with the following.

esphome:
  name: $node_name
  comment: $device_name
  on_boot:
    priority: -100
    then:
      - wait_until: 
          api.connected:
      - delay: 5s
      - lambda: |- 
          if (id(movie_lighting).state) {
            id(disp1).set_component_value("movie",1);
          } else {
            id(disp1).set_component_value("movie",0);
          }
      - component.update: disp1

Repeat the above lambda for any HA state you wish to synchronize on boot with a Nextion object. This is the same code as provided earlier, merely pasted into the on_boot section of yaml. The key is to wait for the HA API to connect and grab current entity state. The delay is added with little further testing and based on log timestamps, could likely be reduced.

Now with all buttons reflecting HA entity state on change or reboot, the next goal was to figure out what to do with the second button on the NSPanel. Since we have no “presence based screen brightness” at the time of writing, it makes sense to use it for that. And I use to turn the display on or off.

This is probably the hackiest of approaches since I follow relay_2 state (which has no physical wire connected) since it appears the button turns on and off with a single press. Feedback welcome!

  - platform: gpio
    name: $device_name Right Button
    pin:
      number: 27
      inverted: true
    on_click:
      then:
        - if:
            condition:
              lambda: 'return id(relay_2).state;'
            then:
              - number.set:
                  id: brightness
                  value: 0
            else:
              - number.set:
                  id: brightness
                  value: 20
        - switch.toggle: relay_2

The results in action;

22 Likes

can you share yours files?

1 Like

will definitely post when done. Have completely blown away my HMI and updated with rounded corners, bezeled edges and started working on pages. Have lots of pages work to do before posting. Getting text spacing up from the bottom to look nicer is solved too! The biggest challenge and only difference in yaml is posted in the OP.

1 Like

Great work! I’m going down a very similar route. Are you using s single image and the “crop image” function for your buttons? I can’t decided if it’s more efficient than slicing everything up. I’ve just spent the afternoon grabbing about 100 MD icons and recolouring them in Photoshop.

Currently using sta set to image and changing colors of the MDI SVG’s in Affinity Photo. Only takes a few moments for each one. I did see the option pop up to crop which I was unware of at the time and have yet to experiment. Which I would do if I could just get goto_page() to work! arg…

Don’t you just put page [id] in the box for the button? I had it working on one test HMI that I was playing with.

That’s what the YT videos show. And that works in the debug simulator of the Nextion Editor. Just didn’t work once uploaded to my NSPanel.

Plus the fact that using goto_page() brings me to a phantom page that doesn’t exist in the TFT, I’m at a loss.

Forgive me if I’m teaching you to suck eggs but have you got the box ticked for “Send component ID” on the button you’re using to change page?

Tried that checked and unchecked. As well as page number vs name. And Press vs Release Event.

ESPHome debug log catches the button press. When the simplest of things does not work, I question life…

…as you can see from the debug demo, it works in the editor. So I am assuming that means that the code is correct.

Yup, I’m a complete spaceman. Scp’ing my tft to the wrong HA path after reopenning WinSCP and not paying attention to the folder path… There was no phantom page, just me being a moron!

1 Like

All been there! I run 2 instances of HA for my home and a holiday place. I was just starting with the one in the holiday place and messed something up. So, whilst logged into my home one I restored a snapshot.

I’ve just sliced all my weather icons so they match the background on my home page. I’m desperate to get this all done but I’m away for 3 days now.

1 Like

Old power cord + Wago connectors + a spare 1 gang box = portable NSPanel :stuck_out_tongue:

yaml and page posted GitHub - crlogic/HA-ESPHome-NSPanel: My work on the Sonoff NSPanel

After adding pages to my HMI it looks like all HA icon states are lost on page change. So you end up needing to refresh these after numerous scenarios;

  • on_setup for tft updates
  • on_boot for reboots
  • on page change
  • maybe sleep? further testing needed
1 Like

May wish you look as the customer UI too;

Thank you so much for posting this. I was trying to do exactly what you’ve done and you did a great job of spelling out how to do it.

One issue I’m running into that I’m hoping you can at least give me some ideas as to what to look into: I have everything working great where a change to a switch in HA updates the button on the NSpanel. The problem I’m running into is that touching the button on the NSpanel only updates HA once. After that, it will never work again. I’m getting events “Got touch page=0 component=6 type=PRESS” but the HA service isn’t being called.

Thoughts? Have you run into this?

Haven’t had that problem. Perhaps post the offending yaml and nextion code?

I think I just found the issue. This is a known problem and is the result of not checking “Send component ID” on Touch Release. Issue resolution.

Thanks for the quick turnaround. I should have googled it first.

That’ll bite ya, enjoy!

1 Like