Intro

The Sonoff NSPanel is a fancy little combination of a dual-gang switch and a control panel. Out of the box it allows for a lot of customisation via the eWeLink App, giving you control of more than just the 2 devices you connect to its relays.

However, since we – as Home Assistant enthusiasts – prefer to manage our own device and not be dependant on a 3rd party app, we’re going to flash the NSPanel with Tasmota :) Note that ESPHome is also an option, since the NSPanel is equiped with an ESP32. I might do a blog post about that as well some day.

Luckily, Blakadder has done a terrific job of tearing down the insides of the NSPanel and identifying the ESP32 chip and pins needed to flash the device. He has also laid the foundation for flashing Tasmota on the NSPanel, which projects like NSPanel Lovelace UI have built further upon.

In this blog post, I’ll discuss my experiences with installing NSPanel Lovelace UI on my NSPanel. This will also include installing AppDaemon in Docker using docker-compose since I’m running HA Container.

Since I switched to using Podman in Part 12 of the Home Assistant Container series, I’ll be sharing my Podman config instead of the Docker-compose config you can find in the NSPanel Lovelace docs.

Preparations

Get Home Assistant Long-Lived Access Token

The AppDaemon container will connect to our Home Assistant instance. To authenticate, it will need a Long-Lived Access Tokens.

  • Login on Home Assistant as the user AppDaemon will authenticate as
  • Click on your username at the bottom-left
  • Scroll to the bottom, where you’ll find the “Long-Lived Access Tokens” section
  • Create a new token, give it a name, and copy the generated token (it will only be displayed once!)
Long-Lived Access Tokens in Home Assistant
Long-Lived Access Token

Keep this token in a txt file or in your password manager, you’ll need it later!

Create MQTT user(s)

In part 4 of our HA Container series we set up the Mosquitto MQTT broker and created and account for Home Assistant.

We’ll add 2 more accounts: 1 for AppDaemon and 1 for Tasmota devices. Make sure you write down the passwords somewhere.

podman exec -it mosquitto mosquitto_passwd /mosquitto/config/mqttuser appdaemon_mqtt
# Enter password
# Repeat password
podman exec -it mosquitto mosquitto_passwd /mosquitto/config/mqttuser tasmota_mqtt
# Enter password
# Repeat password

Swap podman for docker in the commands above if necessary.

! If you’re copying the command from the Mosquitto blog post, make sure you drop the -c parameter as that will wipe out any existing accounts.

AppDaemon installation and configuration

This could be an additional post in the HA Container series as this point, as it’s sort of a repetition of what we’ve done multiple times before.

Podman configuration

Have a look at HA Container Series pt12 if you want to learn more about the config below.

It’s of the utmost importance you mount the AppDeamon config folder inside the HA config folder! This does mean the HA config volume needs to be labeled with a lowercase :z (in the HA container and the AppDaemon config) to indicate this volume is shared.

~/.config/systemd/user/container-appdaemon.service

[Unit]
Description=AppDaemon in a container
Wants=network-online.target
After=network-online.target
Requires=container-homeassistant.service
After=container-homeassistant.service
Requires=container-mosquitto.service
After=container-mosquitto.service
RequiresMountsFor=%t/containers

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n TZ=Europe/Brussels
Restart=on-failure
RestartSec=30
TimeoutStopSec=70
ExecStartPre=-/usr/bin/podman secret create appdaemon-secrets /srv/hass/secret/appdaemon-secrets
ExecStartPre=/bin/rm -f %t/%n.ctr-id
ExecStart=/usr/bin/podman run \
                          --cidfile=%t/%n.ctr-id \
                          --cgroups=no-conmon \
                          --rm \
                          --sdnotify=conmon \
                          --replace \
                          --detach \
                          --label "io.containers.autoupdate=registry" \
                          --name appdaemon \
                          --group-add keep-groups \
                          --network=slirp4netns:allow_host_loopback=true \
                          --volume=/srv/hass/hass/appdaemon:/conf:z \
                          --env TZ="Europe/Brussels" \
                          --env HA_URL="!secret ha_url" \
                          --env TOKEN="!secret ha_token" \
                          --env DASH_URL="!secret dash_url" \
                          --secret=appdaemon-secrets,target=/conf/secrets.yaml \
                          -p 5050:5050 \
                          docker.io/acockburn/appdaemon:latest
ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id
ExecStopPost=-/usr/bin/podman secret rm appdaemon-secrets
Type=oneshot
NotifyAccess=all

[Install]
WantedBy=default.target

/srv/hass/secret/appdaemon-secrets

ha_url: http://10.0.2.2:8123
ha_token: "<Long-Lived Access Token>"
mqtt_host: 10.0.2.2
mqtt_user: appdaemon_mqtt
mqtt_pass: <appdaemon_mqtt password>
dash_url: http://<ip.of.our.box>:5050

Don’t forget to create the directory for the container.

mkdir /srv/hass/hass/appdaemon

Don’t start the container just yet, we want to make a few more changes to the AppDaemon config first.

Install Babel dependency

Babel is used by AppDaemon for localization (e.g. date formats).

We can add this as a Python dependency by adding it to our requirements.txt. This file most likely doesn’t exist yet and must be created inside the container’s /conf folder.

echo "Babel" >> /srv/hass/hass/appdaemon/requirements.txt

If you had already started the container, a restart of the container will ensure the dependency is discovered and installed.

systemctl --user restart container-appdaemon.service

AppDaemon app configuration

The main configuration for AppDaemon is located at /srv/hass/hass/appdaemon/appdaemon.yaml or the appdeamon folder inside the HA container.

Copy/paste this config there to ensure MQTT is set up and our secrets from earlier are being used.

---
secrets: /conf/secrets.yaml  # IMPORTANT! Otherwise !secret won't work
appdaemon:
  latitude: ##.##########
  longitude: ##.##########
  elevation: 30
  time_zone: !env_var TZ
  plugins:
    HASS:
      type: hass
      ha_url: !secret ha_url
      token: !secret ha_token
    MQTT:
      type: mqtt
      namespace: mqtt
      client_id: "appdaemon"
      client_host: !secret mqtt_host
      client_port: 1883
      client_user: !secret mqtt_user
      client_password: !secret mqtt_pass
      client_topics: NONE
http:
  url: !secret dash_url
admin:
api:
hadashboard:

Start the container

Enable the service to start the container at boot, and start it.

systemctl --user enable --now container-appdaemon.service

After running the command, the container image will be pulled and the container will be started. The AppDaemon dashboard will be available at http://<ip.of.our.box>:5050. Some default configurations will be added to the /conf folder inside the container as well.

Minimal NSPanel config

The configuration for the NSPanel is stored in an AppDeamon app.

Below is a minimal config to get started. Store this config in /srv/hass/hass/appdaemon/apps/apps.yaml. Delete the hello-world app if it’s present.

---
nspanel-1:
  module: nspanel-lovelace-ui
  class: NsPanelLovelaceUIManager
  config:
    panelRecvTopic: "tele/tasmota_<your_mqtt_topic>/RESULT"
    panelSendTopic: "cmnd/tasmota_<your_mqtt_topic>/CustomSend"
    model: eu

Modify <your_mqtt_topic> to something specific for your NSPanel and keep note as we’ll need it later.

You can add multiple configs for multiple NSPanels. Make sure you use unique MQTT topics for each.

Install Lovelace AppDaemon Backend

Enable AppDeamon automations in HACS

Next, we’ll install the Lovelace AppDaemon Backend application in Home Assistant. We’ll install this from HACS.

We’ll assume you already have HACS installed. This blog post won’t go into details on how to install HACS.

  1. Go to Settings > Devices & Services in Home Assistant.
  2. Press the Configure button under the HACS integration.
  3. Ensure Enable AppDaemon apps discovery & tracking is selected.
Enable 'Enable AppDaemon apps discovery & tracking' in HACS config
Enable AppDaemon apps discovery & tracking
  1. Open the HACS store.
  2. Go to the Automations section.
  3. Click Explore & download repositories.
  4. Search for NSPanel Lovelace UI Backend.
  5. Download the repository.
Download the 'NSPanel Lovelace UI Backend' repository
NSPanel Lovelace UI Backend

Flash Tasmota on NSPanel

Finally we’ll flash Tasmota on the NSPanel.

You’ll need to disassemble the NSPanel using a flathead screwdriver or a prying tool and a small Philips head screwdriver. The Wowstick comes in handy here.

You can check how to open the Touch Plate and what the pinout of the PCB is in Blakadder’s teardown.

NSPanel pinout
Pinout of the NSPanel

Serial connection to the NSPanel

  1. Set your serial adapter to 3.3V
  2. Connect it to the GND, ESP_RX, ESP_TX, and 3V3 pins on the PCB (marked yellow in the picture above).
    Make sure you cross RX-TX between the NSPanel and the FTDI.
  3. Grab a Dupont wire and connect it between the IO0 and one of the GND pins at the right (next to the 5V pins) to force the NSPanel to boot in flash mode.
  4. While putting some pressure on the Dupont wires to ensure a good connection, connect the FTDI to your computer.
Picture of FTDI connected to the NSPanel
Connect FTDI to NSPanel

Flash Tasmota

  1. Open the Tasmota Web Installer in Chrome or Edge as the browser needs to be able to connect to your serial device.
  2. Select Tasmota32 Sonoff-NSPanel (english).
  3. Press Connect.
  4. In the pop-up, select your serial device.
  5. Flash Tasmota.
Tasmota Web Installer
Tasmota Web Installer

Connect NSPanel to your WiFi

You can connect the NSPanel with your WiFi via the captive portal that is now active on the NSPanel or via the Web Installer.

  1. Disconnect the FTDI from the NSPanel
  2. Set the FTDI to 5V
  3. Connect GND and VCC on the FTDI to 5V and GND on the NSPanel (marked red in the pinout above)
  4. Connect RX and TX on the FTDI to ESP_TX and ESP_TX on the NSPanel (marked yellow)
  5. Do NOT connected GPIO0 to GND!
  6. In the Tasmota Web Installer, reconnect to your device
  7. Select the option to connect to WiFi
  8. Pick the correct SSID and enter the password

Configure Tasmota for NSPanel

The final configuration of the NSPanel is done via the Tasmota Web UI. Check in your router’s DHCP table which IP the NSPanel has.

You can also assemble the NSPanel again and power it from mains as we won’t need access to the PCB anymore.

NSPanel template

  1. Go to Configuration
  2. Pick Configure Other
  3. Paste the following template in the input field:
    {"NAME":"NSPanel","GPIO":[0,0,0,0,3872,0,0,0,0,0,32,0,0,0,0,225,0,480,224,1,0,0,0,33,0,0,0,0,0,0,0,0,0,0,4736,0],"FLAG":0,"BASE":1,"CMND":"ADCParam1 2,11200,10000,3950 | Sleep 0 | BuzzerPWM 1"}
    
  4. Tick the Activate checkbox
  5. Save

The NSPanel will now reboot and the main screen will display 2 toggles for the relays and the read-out of the temperature sensor.

Berry Driver

Console

Execute the following command in the Tasmota Console to download the Berry Driver and reboot the device:

Backlog UrlFetch https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be; Restart 1

Manually

  1. Download the Berry Driver from the NSPanel Lovelace UI repository and save as autoexec.be.
  2. On the Tasmota Web UI go to Consoles > Manage File System.
  3. Upload the autoexec.be you just downloaded.
  4. Reboot the NSPanel

Flash the firmware

  1. Go to Consoles > Console.
  2. Paste the following command in the input field and press Return to run it:
    FlashNextion http://nspanel.pky.eu/lui-release.tft
    

The NSPanel should now display a loadscreen showing it’s downloading and flashing the TFT. This will also be reflected in the Logs.

22:34:43.031 CMD: FlashNextion http://nspanel.pky.eu/lui-release.tft
22:34:43.042 RSL: RESULT = {"FlashNextion":"Done"}
22:34:43.082 FLH: host: nspanel.pky.eu, port: 80, get: /lui-release.tft
22:34:44.467 FLH: Something has gone wrong flashing display firmware [bytes('1AFFFFFF')]
22:34:44.569 FLH: Send (High Speed) flash start
22:34:44.974 RSL: RESULT = {"Flashing":{"complete": 0, "time_elapsed": 0}}
22:34:48.522 RSL: RESULT = {"Flashing":{"complete": 1, "time_elapsed": 3}}
22:34:51.460 RSL: RESULT = {"Flashing":{"complete": 2, "time_elapsed": 6}}
22:34:54.398 RSL: RESULT = {"Flashing":{"complete": 3, "time_elapsed": 9}}
22:34:57.341 RSL: RESULT = {"Flashing":{"complete": 4, "time_elapsed": 12}}
22:35:00.594 RSL: RESULT = {"Flashing":{"complete": 5, "time_elapsed": 16}}
22:35:03.532 RSL: RESULT = {"Flashing":{"complete": 6, "time_elapsed": 18}}
22:35:06.460 RSL: RESULT = {"Flashing":{"complete": 7, "time_elapsed": 21}}
22:35:09.393 RSL: RESULT = {"Flashing":{"complete": 8, "time_elapsed": 24}}
22:35:12.364 RSL: RESULT = {"Flashing":{"complete": 9, "time_elapsed": 27}}
22:35:15.593 RSL: RESULT = {"Flashing":{"complete": 10, "time_elapsed": 31}}

If your NSPanel doesn’t have a (reliable) connection to the internet, you can download the lui-release.tft locally and use your own webserver, like the one from Home Assistant.

  1. Download http://nspanel.pky.eu/lui-release.tft
  2. Copy this file to /srv/hass/hass/www/ (the www folder in Home Assistant)
  3. Run the same command, but referring to Home Assitant:
    FlashNextion http://<ip.of.you.ha>:8123/local/lui-release.tft

Set up MQTT in Tasmota

Via the Tasmota Web UI, set up MQTT:

  1. Go to Configuration
  2. Go to Configure MQTT
  3. Enter your MQTT information, such as the host, client and password, and topic
    Do NOT modify the Full Topic (%prefix%/%topic%/)
Tasmota MQTT config
Tasmota MQTT config

Configure views in NSPanel Lovelace UI

Now we can start setting up our Lovelace dashboards for the NSPanel.

To modify the views, make changes to the apps.yaml file. Each card is a view/page/dashboard between which you can scroll.

---
nspanel-bureau:
  module: nspanel-lovelace-ui
  class: NsPanelLovelaceUIManager
  config:
    panelRecvTopic: "tele/tasmota_<your_mqtt_topic>/RESULT"
    panelSendTopic: "cmnd/tasmota_<your_mqtt_topic>/CustomSend"
    updateMode: "auto-notify"
    model: eu
    locale: "nl_NL"
    screenBrightness: 100
    sleepBrightness:
      - time: "8:45:00"
        value: 10
      - time: "18:15:00"
        value: 0
    sleepOverride:
      entity: light.desk
      brightness: 20
    screensaver:
      entity: weather.forecast_home
      theme:
        autoWeather: true
    cards:
      - type: cardThermo
        title: Office
        entity: climate.trv_office
      - type: cardThermo
        title: Living Room
        entity: climate.resideo_thermostat
      - type: cardEntities
        title: Office
        entities:
          - entity: light.office
          - entity: light.desk
          - entity: input_boolean.in_call

Check the documentation for more info.

Mount the NSPanel

When you’re certain your desk setup is working, it’s time to mount the NSPanel to its final location. In my case, I decided to swap the light switch of my office for the NSPanel.

First, I had to remove the old wall-box as it was too small and didn’t support screw-installation faceplates. Once that was done, I could wire up the NSPanel, which was really easy to do.

Connect the Live wire that used to go to the switch, to the L terminal. Neutral goes to N. The switched Live from the light went to the S2 terminal in my case, as that matches the right-side button which made most sense in my setup.

I didn’t have a 2nd light to connect to the other relais output, but I’m planning to use that button as a trigger for an automation.

NSPanel installed on the wall, showing the weather screensaver
NSPanel installed on the wall

Yes, the protective cover is still on the screen (:

Upgrading NSPanel

Every now and then an update for NSPanel Lovelace UI becomes available through HACS.

After installing that update, you’ll need to restart the AppDeamon container.

systemctl --user restart container-appdaemon.service

Your NSPanel will shortly after that restart notify you an update is available for it and will ask you whether you want to install it. Of course you should click Yes there!

Clicking Yes will download and flash the latest TFT onto the NSPanel.

IF your NSPanel can’t access the internet, or the flashing doesn’t seem to work for some reason, have a look at Flash the firmware on how to do it from a local webserver.

Troubleshooting

Here are some issues I encountered while flashing my NSPanel so I thought it’d be a good idea to share this info with you.

TFT not downloading

If you don’t see the TFT being downloaded when running the FlashNextion command, it can help to reflash Tasmota.

  1. Go to Firmware Upgrade in the Tasmota web portal.
  2. Enter the following OTA URL: http://ota.tasmota.com/tasmota32/release/tasmota32-nspanel.bin.
  3. Press Start Upgrade.
  4. Wait about 5 minutes until the firmware reinstall is finished.

Once the reinstall is finished, repeat the steps from the Configure Tasmota for NSPanel section.

Date on screensaver doesn’t match my config

If you’re using dateFormatBabel and/or locale in your apps.yaml to configure the datetime format on your screensaver, you need to confirm Babel is installed on your system. If not, the screensaver will display the date using the default datetime format.

See Install Babel dependency.

The AppDaemon logs should womething like:

Collecting Babel
  Downloading Babel-2.11.0-py3-none-any.whl (9.5 MB)
Requirement already satisfied: pytz>=2015.7 in /usr/local/lib/python3.9/site-packages (from Babel->-r /conf/requirements.txt (line 1)) (2021.3)
Installing collected packages: Babel
Successfully installed Babel-2.11.0

Alternatively, you can configure the date using dateFormat and the Python Date Format Codes. However, the displayed date will not be localised.

---
nspanel-1:
  config:    
    # Fri 18 November
    dateFormatBabel: "EE d MMMM"
    
    # vrijdag 18 november 2022
    locale: "nl_NL"
    
    # vr 18 november
    locale: "nl_NL"
    dateFormatBabel: "EE d MMMM"

    # Friday 18 November 2022
    dateFormat: "%A %d %B %Y"