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;
- match the 3x3 grid layout of the Lovelace dashboard on the tablet
- synchronize on/off state using the same mechanism of changing icon background
- 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;