Sonoff NSPanel by ITead - Smart Scene Wall Switch based on ESP32 and custom Nextion Touch Screen Panel Display (non-Pro variant)

Thank you for your time, Christopher.

I am not sure I understang what you mean by the external components. Do you mean the tag external_components refering to pr#2956? I am not sure how this works, but I guess it has to do with the Protocol Reparse Mode change of the Nextion TFT? The tag is in the code.
Here is the code below (I started copying your code, then I tried out this one which is added lately in your git page).

substitutions:
  device_name: nspanel-dev

# Example config.yaml
esphome:
  name: nspanel
  comment: $device_name

esp32:
  board: esp32dev

wifi:
  ssid: fha2
  password: '32323245'

time:
  - platform: homeassistant
    id: homeassistant_time
    on_time:
      - seconds: 0
        minutes: /1
        then:
          - lambda: id(disp1).set_component_text_printf("Home.time", "%02i:%02i", id(homeassistant_time).now().hour, id(homeassistant_time).now().minute);
          - lambda: id(disp1).set_component_text_printf("Home.date", "%i-%02i-%02i", id(homeassistant_time).now().year, id(homeassistant_time).now().month, id(homeassistant_time).now().day_of_month);
          - lambda: id(disp1).set_component_text_printf("Home.temp", "%.1f", id(current_temperature).state);
          - lambda: id(disp1).set_component_text_printf("Home.tempfl", "%.1f", id(temp_feelslike).state);

api:
  services:
    # Service to play a song
    - service: play_rtttl
      variables:
        song_str: string
      then:
        - rtttl.play:
            rtttl: !lambda 'return song_str;'
    - service: upload_tft
      then:
        - lambda: 'id(disp1)->upload_tft();'

logger:
  baud_rate: 0
  level: DEBUG

ota:

uart:
  tx_pin: 16
  rx_pin: 17
  baud_rate: 115200
  id: tf_uart
#  debug:
#    direction: BOTH
#    dummy_receiver: false
#    after:
#      delimiter: "\n"
#    sequence:
#      - lambda: UARTDebug::log_string(direction, bytes);

external_components:
  - source: github://pr#2956
    components: [nextion]
    refresh: 1h

# A reboot button is always useful
button:
  - platform: restart
    name: $device_name Restart    

binary_sensor:
  - platform: gpio
    name: $device_name Left Button
    pin:
      number: 14
      inverted: true
    on_click:
      - switch.toggle: relay_1

  - platform: gpio
    name: $device_name Right Button
    pin:
      number: 27
      inverted: true
    on_click:
      - switch.toggle: relay_2
      
  - platform: nextion
    name: $device_name Music previous
    page_id: 1
    component_id: 1

  - platform: nextion
    name: $device_name Music play pause
    page_id: 1
    component_id: 2

  - platform: nextion
    name: $device_name Music next
    page_id: 1
    component_id: 3

  - platform: nextion
    name: $device_name Music cast
    page_id: 1
    component_id: 4

  - platform: nextion
    name: $device_name Music voldown
    page_id: 1
    component_id: 9
    
  - platform: nextion
    name: $device_name Music volup
    page_id: 1
    component_id: 10
    
  - platform: nextion
    name: $device_name Playlist cocktail
    page_id: 1
    component_id: 11

  - platform: nextion
    name: $device_name Playlist food
    page_id: 1
    component_id: 12
    
  - platform: nextion
    name: $device_name Playlist dance
    page_id: 1
    component_id: 13

  - platform: nextion
    name: $device_name Playlist note
    page_id: 1
    component_id: 14    

output:
  - platform: ledc
    id: buzzer_out
    pin:
      number: 21

switch:
  - platform: gpio
    name: $device_name Relay 1
    id: relay_1
    pin:
      number: 22

  - platform: gpio
    name: $device_name Relay 2
    id: relay_2
    pin:
      number: 19

  - platform: gpio
    name: $device_name Screen Power
    id: screen_power
    entity_category: config
    pin:
      number: 4
      inverted: true
    restore_mode: ALWAYS_ON

  - platform: template
    name: $device_name Energy Saving Mode
    id: eco_mode
    entity_category: config
    restore_state: true
    optimistic: true

rtttl:
  id: buzzer
  output: buzzer_out

sensor:
  - platform: adc
    id: ntc_source
    pin: 38
    update_interval: 10s
    attenuation: 11db

  - platform: resistance
    id: resistance_sensor
    sensor: ntc_source
    configuration: DOWNSTREAM
    resistor: 11.2kOhm

  - platform: ntc
    id: temperature
    sensor: resistance_sensor
    calibration:
      b_constant: 3950
      reference_temperature: 25°C
      reference_resistance: 10kOhm
    name: $device_name Temperature
    
  - platform: uptime
    name: $device_name Uptime Sensor
    id: uptime_sensor
    update_interval: 60s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            state: !lambda |-
                int seconds = round(id(uptime_sensor).raw_state);
                int days = seconds / (24 * 3600);
                seconds = seconds % (24 * 3600);
                int hours = seconds / 3600;
                seconds = seconds % 3600;
                int minutes = seconds /  60;
                seconds = seconds % 60;
                return (
                  (days ? to_string(days) + "d " : "") +
                  (hours ? to_string(hours) + "h " : "") +
                  (minutes ? to_string(minutes) + "m " : "") +
                  (to_string(seconds) + "s")
                ).c_str();

  - platform: wifi_signal
    name: $device_name WiFi Signal Sensor
    update_interval: 60s
    
  - platform: nextion
    id: screen_current_page
    variable_name: dp
    
  - platform: homeassistant
    id: current_temperature
    entity_id: weather.home
    attribute: temperature
    on_value:
      # Push it to the display
      then:
        - lambda: id(disp1).set_component_text_printf("Home.temp", "%.1f", id(current_temperature).state);
        
  - platform: homeassistant
    id: temp_feelslike
    entity_id: sensor.home_temperature_feels_like
    on_value:
      # Push it to the display
      then:
        - lambda: id(disp1).set_component_text_printf("Home.tempfl", "%.1f", id(temp_feelslike).state);

text_sensor:
  - platform: template
    name: $device_name Uptime Human Readable
    id: uptime_human
    icon: mdi:clock-start

  - platform: version
    name: $device_name ESPHome Version

  - platform: homeassistant
    id: music_artist
    entity_id: media_player.sonos_vardagsrum
    attribute: media_artist
    on_value:
      then:
        - lambda: id(disp1).set_component_text_printf("Music.music_sn", "%s", id(music_artist).state.c_str());

  - platform: homeassistant
    id: music_title
    entity_id: media_player.sonos_vardagsrum
    attribute: media_title
    on_value:
      then:
        - lambda: id(disp1).set_component_text_printf("Music.music_an", "%s", id(music_title).state.c_str());

  - platform: homeassistant
    id: sun_sun
    entity_id: sun.sun

  - platform: homeassistant
    id: weather_symbol
    entity_id: weather.home
    on_value:
      then:
        - lambda: |-
            int symbol=5; // 5 is a empty box.
            if (id(weather_symbol).state == "clear-night") {
              symbol=6;
            } else if (id(weather_symbol).state == "cloudy") {
              symbol=7;
              if (id(sun_sun).state == "below_horizon") {
                symbol=8;
              }
            } else if (id(weather_symbol).state == "fog") {
              symbol=9;
            } else if (id(weather_symbol).state == "hail" || id(weather_symbol).state == "snowy-rainy") {
              symbol=10;
            } else if (id(weather_symbol).state == "lightning") {
              symbol=11;
            } else if (id(weather_symbol).state == "lightning-rainy" || id(weather_symbol).state == "exceptional") {
              symbol=12;
              if (id(sun_sun).state == "below_horizon") {
                symbol=13;
              }
            } else if (id(weather_symbol).state == "partlycloudy") {
              symbol=14;
              if (id(sun_sun).state == "below_horizon") {
                symbol=8;
              }
            } else if (id(weather_symbol).state == "pouring") {
              symbol=15;
            } else if (id(weather_symbol).state == "rainy") {
              symbol=16;
            } else if (id(weather_symbol).state == "snowy") {
              symbol=17;
            } else if (id(weather_symbol).state == "sunny") {
              symbol=18;
            } else if (id(weather_symbol).state == "windy" || id(weather_symbol).state == "windy-variant") {
              symbol=19;              
            }
            id(disp1).send_command_printf("Home.weather_symbol.pic=%i", symbol);
    
number:
  platform: template
  name: $device_name Brightness
  id: brightness
  entity_category: config
  unit_of_measurement: '%'
  min_value: 0
  max_value: 100
  step: 1
  initial_value: 30
  set_action:
    then:
      - lambda: 'id(disp1).set_backlight_brightness(x/100);'
      
# Configure the screen itself
display:
  - platform: nextion
    id: disp1
    uart_id: tf_uart
    tft_url: 'http://10.0.0.120:8123/local/hmi.tft'
    # A little fun...
    on_setup:
      then:
        - number.set:
            id: brightness
            value: 30

#        - rtttl.play: "twobits:d=4,o=5,b=220:c6,8g,8g,a,g,p,b,c6"

Do you spot any obvious error?

Best regards,
Felix

Hi Christopher,
You were right, I wasnt importing the http file. The mistake is that I omitted the quotes around the http link. In my last email I had added them, but hadnt pressed “OTA import” yet.
Now it works like a charm, I imported your video yaml (with the “GO red!” button), and it worked at first try…

I am very thankful that you spent some time on that issue…believe me I spent most of my day figuring out what I had done wrong. Now it is solved, I can focus on making my scheme.

All the best, and please continue doing videos!!
Felix

More progress

Working on a thermostat

Don’t have the coding done just the start of the TFT

Anyone have any idea how to best implement the esp side of it?

20220110_171538_1

3 Likes

You are right, there are also 68 mm electrical boxes around in the EU. But 60 mm are also pretty popular
A friend of mine also measured his box and it’s also 60 mm in diameter.

Maybe 60 mm is only popular in Germany?

I’m sure I will not be the only person with this problem and I just try to warn people with 60 mm boxes :slight_smile:

1 Like

@botts Cool! I have not tried this myself yet but have some thoughts. I assume the command set provided in ESPHome is not sufficient to read the component type you are using for this? If so, would it be possible to

  • Create a empty HMI page and add a text/number component to that.
  • Make the field above global
  • Add a code snippet to the graph and button components to push the thermostat graph’s value to the text/number field
  • Fetch that components value to ESPHome
    That would need to be polled which is not ideal. If it is possible (I have not tested) to push a button/touch area from the Nextion script, then the script mentioned earlier could push a component on the hidden page which would notify ESPHome to pull the data? On the other hand, a thermostat within a home is a rather slowly acting component, so even if it is polled once a minute, that might be sufficient?

Not the shortest path, but I would try something like that if it cannot be done directly.

On a general note I think it’s very nice to see that there is alot of progress with multiple components. I have refined my own template with further functions (light control, alarm mode and bootup screen) and will publish it on Github within shortly. As we are many persons working with basically the same, but with different specific functionality, I think it would be great to try to collect the different functions into one project (HMI project and ESPHome config). That way it is rather simple for everybody to pick the components they want to create their own system. :slight_smile:

2 Likes

My contribution today is investigating Nextion display objects pic/crop changes;

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

Code here

Have you set the HMI components to global? I think that is required to persist data on page changes.

1 Like

@marcfager on the Up and down arrows i have added the below code to the Touch Press Events

if(j0.val<89)
{
  j0.val++
  j0.val++
  j0.val++
}
if(n0.val<30)
{
  n0.val++
}

which updates the value of the set temp displayed number

Just cant find away to push this the home assistant.

So…

I changed it to a text box and can update my home assistant climate by adding

binary_sensor:
  - platform: nextion
    name: $device_name Temp Up
    id: temp_up_button    
    page_id: 8
    component_id: 3  
    on_press:
      then:
        if:
          condition:
            lambda: 'return id(temp_up_binary).state;'
          then:
            - lambda: id(disp1).set_component_text_printf("ThermSlide.t2", "%.1f", id(set_temp).state);   
            - component.update: disp1                 
    
  - platform: nextion
    name: $device_name Temp Down
    id: temp_down_button    
    page_id: 8
    component_id: 4     
    on_press:
      then:
        if:
          condition:
            lambda: 'return id(temp_down_binary).state;'
          then:
            - lambda: id(disp1).set_component_text_printf("ThermSlide.t2", "%.1f", id(set_temp).state);  
            - component.update: disp1 
sensor:
  - platform: homeassistant
    id: set_temp
    entity_id: climate.climate
    attribute: temperature
    on_value:
      # Push it to the display
      then:
        - lambda: id(disp1).set_component_text_printf("ThermSlide.t2", "%.1f", id(set_temp).state);  

This has the up and down arrows controlling the temp on the climate and displaying the set temp on the NSPanel.

Just dont know how to sync the slider with the up and down arrow or sync the slider with the value of the set temp in home assistant

Any ideas on using the number box instead?20220111_004953_1

to make the climate increase on each button press i had to create 2 scripts and automation to trigger them

alias: NSpanel - Increase Temp by 1
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.nspanel_1_temp_up
    to: 'on'
condition: []
action:
  - service: script.turn_on
    target:
      entity_id: script.climate_heat_up_by1
mode: single

alias: NSpanel - Decrease Temp by 1
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.nspanel_1_temp_down
    to: 'on'
condition: []
action:
  - service: script.turn_on
    target:
      entity_id: script.climate_down_by_1
mode: single
alias: Climate Heat Down by 1
sequence:
  - service: climate.set_temperature
    data_template:
      entity_id: climate.climate
      temperature: '{{(state_attr(''climate.climate'' , ''temperature'')|round(0)) - 1 }}'
mode: single
icon: mdi:download

alias: 'Climate Heat Up by 1 '
sequence:
  - service: climate.set_temperature
    data_template:
      entity_id: climate.climate
      temperature: '{{(state_attr(''climate.climate'' , ''temperature'')|round(0)) + 1 }}'
mode: single
icon: mdi:upload

Not sure how to do this is esphome ???

As we are many persons working with basically the same, but with different specific functionality, I think it would be great to try to collect the different functions into one project (HMI project and ESPHome config). That way it is rather simple for everybody to pick the components they want to create their own system. :slight_smile:

Any ideas how we can do this?

I think I played with global but forget the outcome. Will test more.

[edit] lol, that was easy. global solves it. Thanks!

I have been trying to minimize HA scripts/automations needed. What about the nextion up/down button change the HA climate temperature directly (not the Nextion object). Then also have the HA climate temp as a sensor in ESPHome and that ESPHome sensor perform the set_component_text_printf("ThermSlide.t2"... on_state: … ?

Not sure if I made sense or not…

The most confusing part for is understanding what yaml is ESPHome and what yaml is Home Assistant script/automation. And what object is ESPHome vs HA vs Nextion. I am sure others are less cross-eyed than me.

I have HA controlling displayed temp

just not sure how to control ha climate temp attribute from esphome?

So what about replacing the on_press from;
[ESPHome yaml]

binary_sensor:
  - platform: nextion
    name: $device_name Temp Up
    id: temp_up_button    
    page_id: 8
    component_id: 3  
    on_press:
      then:
        if:
          condition:
            lambda: 'return id(temp_up_binary).state;'
          then:
            - lambda: id(disp1).set_component_text_printf("ThermSlide.t2", "%.1f", id(set_temp).state);   
            - component.update: disp1 

with something like this where you use a HA service;
[ESPHome yaml]

binary_sensor:
  - platform: nextion
    name: $device_name Bedlight
    id: bedlight
    page_id: 0
    component_id: 3
    internal: true
    on_press:
      then:
        if:
          condition:
            lambda: 'return id(bedroomatombulb).state;'
          then:
            - homeassistant.service:
                service: light.turn_off
                data:
                  entity_id: light.bedroom_atom_bulb
          else:
            - homeassistant.service:
                service: light.turn_on
                data:
                  entity_id: light.bedroom_atom_bulb

obviously my light.turn_off is an example; you’d use climate.set_temperature

that only works with a binary sensor as far as Im aware?

I have another thing to try but getting other issues

esphome code

display:
  - platform: nextion
    lambda: |-
      it.set_wait_for_ack(true);
      it.set_component_text("n2_text", id(n2_text).state.c_str());  
text_sensor:
  - platform: homeassistant
    id: n2_text
    entity_id: sensor.nextion_text1

but getting

Too many candidates found for ‘nextion_id’ type ‘nextion::Nextion’ One is ‘disp1’.

when I try to compile

Yes the HMI button would be a binary - it is pressed, then it triggers HA to increase the temp. Hope that makes sense. I haven’t tried it for climate. I use this concept to send an “alarm code” from an HMI keypad to HA.

Good Point

I tried this put does not work

  - platform: nextion
    name: $device_name Temp Up
#    nextion_id: set_nextion 
#    update_interval: 1s      
    id: temp_up_button    
    page_id: 8
    component_id: 3  
    on_press:
#      then:
#        if:
#          condition:
 #           lambda: 'return id(temp_up_binary).state;'
      then:
        - homeassistant.service:
            service: climate.set_temperature
            data:
              entity_id: climate.climate
            data_template:
              temperature: |
                {{(state_attr(''climate.climate'' , ''temperature'')|round(0)) + 1 }}

Does the temp_up_button have both Press and Release Touch events have ‘Send Component ID’ checked in the Nextion Editor?

I had to do that to get a volume up button working. Which updates a volume slider on the NSPanel.

binary_sensor:
  - platform: nextion
    name: $device_name Sonos Volume Up
    id: volumeup
    page_id: 2
    component_id: 6
    internal: true
    on_press:
      then:
        - homeassistant.service:
            service: media_player.volume_up
            data:
              entity_id: media_player.tv_sonos

sensor:
  - platform: homeassistant
    id: ha_tv_volume
    entity_id: media_player.tv_sonos
    attribute: volume_level
    on_value:
       then:
          - lambda: |- 
              const int val = id(ha_tv_volume).state * 100;
              id(disp1).send_command_printf("page2.tvslider.val=%i", val);
          - component.update: disp1   
          - logger.log: "Updated HMI Sonos volume slider"

So while it isn’t very fast. I probably need larger steps (and better PNG), this is a start.

https://imgur.com/a/pHd2qTg

[edit] link

1 Like

got a bit further

cant get slider to change set temp any ideas?

Oof, these yaml’s getting posted are getting long! Can you post just the code you are using for the very specific use-case? Like my earlier post - just the minimal needed.

I find this helps a lot when developing a solution. Simplify and build.

Part of simplifying includes testing out each step individually. So does your HA template work? Does all ESPHome conditions work? etc.

Once stripped down, which part doesn’t work specifically? Including log.logger helps a lot with figuring out when things run. So you can tell if the problem is the section you expect not running or if it is running but there is a syntax problem.