Remote WebView shows HA web dashboard on ESP32-S3-based screen

I am having a similar issue currently. I can get the login screen to show up on the display, and when I inspect it in the Chrome Devtools page, it allows me to type in the login info, but after typing 2 or 3 characters, the screen goes solid white and stays that way.

Edit:

Nevermind, It just kind of stopped doing it after several reboots, and now the white screen problem is no longer present.

I am having a different issue now though, I can’t seem to get any button presses or anything working. The touch input is functioning, because my touch events show up in the log output, and I can scroll the screen just fine. It just doesn’t work for pressing button, selecting anything in the sidebar, etc. There’s just no events or anything happening when I press anything, it’s like I’m not even touching it. It seems weird to me that I can still scroll up and down my dashboard with my finger though :thinking:

I loaded the page through the chrome dev-tools just like I did when I had to log in, and I can’t interact with anything through that page either, I can just scroll up and down the page with my mousewheel.

@travisfinch1983 check this Only scrolling · Issue #11 · strange-v/RemoteWebViewServer · GitHub

I was wondering, if anybody tried to implement a keyboard to type in search fields in dashboards.

Eg to search for a specific artist on Spotify or for entities.

Solved it with a js file. :wink:

i’m getting a lot of:
[20:28:22][D][esp-idf:000][rwv_decode]: E (80659) jpeg.decoder: jpeg_decoder_process(258): jpeg-dma2d handle jpeg decode timeout, please check image accuracy and timeout_ms

and

[20:31:46][W][Remove_WebView:229][websocket_task]: decode queue full, dropping packet

esphome:
  name: jc4880p433c
  friendly_name: JC4880P433C Display

esp32:
  #board: esp32-p4-evboard
  variant: ESP32P4
  cpu_frequency: 400MHz
  flash_size: 16MB
  framework:
    type: esp-idf
    advanced:
      enable_idf_experimental_features: yes
    components:
      - name: "espressif/esp_websocket_client"
        ref: 1.5.0
      - name: "bitbank2/jpegdec"
        source: https://github.com/strange-v/jpegdec-esphome

ota:
  - platform: esphome
    password: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


external_components:
  #- source: github://strange-v/RemoteWebViewClient@main
  - source: github://strange-v/RemoteWebViewClient@hardware_jpeg
    refresh: 0s
    components: [ remote_webview ]
remote_webview:
  id: rwv1
  server: xxx.xxx.xxx.xxx:8081
  url: https://xxxxxxxx/lovelace/lcd-sala1 # set url: "self-test" to initiate the self-test
  #url: "self-test"
  #full_frame_tile_count: 1
  max_bytes_per_msg: 61440
  jpeg_quality: 75
  min_frame_interval: 25

  

 
  big_endian: false   # ← MUDANÇA 
  

esp_ldo:
  - channel: 3
    voltage: 2.5V

psram:
  mode: hex
  speed: 200MHz

preferences:
  flash_write_interval: 5min

esp32_hosted:
  variant: ESP32C6
  reset_pin: GPIO54
  cmd_pin: GPIO19
  clk_pin: GPIO18
  d0_pin: GPIO14
  d1_pin: GPIO15
  d2_pin: GPIO16
  d3_pin: GPIO17
  active_high: true

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap: {}

captive_portal:

api: {}



i2c:
  id: i2c_bus
  sda: GPIO7
  scl: GPIO8
  scan: false
  frequency: 400kHz    # ← MUDANÇA 1

touchscreen:
  platform: gt911
  i2c_id: i2c_bus
  id: my_touchscreen
  reset_pin: GPIO3
  #update_interval: 100ms
  #on_update:
    #then:
    #  - lambda: |-
    #      if (touches.size() > 0) {
    #        auto touch = touches[0];
    #        ESP_LOGI("TOUCH", "X=%d Y=%d", touch.x, touch.y);
    #      }
      
#binary_sensor:
#  - platform: touchscreen
#    touchscreen_id: my_touchscreen
#    name: "Touch Detected"
#    x_min: 0
#    x_max: 480
#    y_min: 0
#    y_max: 800
#    on_press:
#      then:
#        - logger.log: "TOUCH DETETADO!"
#        - delay: 100ms
output:
  - platform: ledc
    pin: GPIO23
    id: backlight_pwm
    frequency: 1000Hz
    min_power: 0 #0.030

light:
  - platform: monochromatic
    name: "Display Backlight"
    output: backlight_pwm
    id: backlight
    restore_mode: RESTORE_DEFAULT_ON

font:
  - file: "gfonts://Roboto"
    id: font_roboto
    size: 32
  - file: "gfonts://Roboto"
    id: font_roboto_large
    size: 48

display:
  - platform: mipi_dsi
    id: my_display
    model: JC4880P443
    dimensions: 480X800
    rotation: 180
    reset_pin: GPIO5
    show_test_card: False
    update_interval: never
    byte_order: little_endian  # ← MUDANÇA 
    

logger:
  level: DEBUG


services:
  rwvserver:
    image: strangev/remote-webview-server:latest  # use :beta for pre-release
    container_name: remote-webview-server
    restart: unless-stopped
    environment:
      TILE_SIZE: 32
      FULL_FRAME_TILE_COUNT: 4
      FULL_FRAME_AREA_THRESHOLD: 0.5
      FULL_FRAME_EVERY: 50
      EVERY_NTH_FRAME: 1
      MIN_FRAME_INTERVAL_MS: 80
      JPEG_QUALITY: 85
      MAX_BYTES_PER_MESSAGE: 14336
      WS_PORT: 8081
      DEBUG_PORT: 9221 # internal debug port
      HEALTH_PORT: 18080
      PREFERS_REDUCED_MOTION: false
      USER_DATA_DIR: /pw-data
      BROWSER_LOCALE: "en-US"
    ports:
      - "8081:8081"                   # WebSocket stream
      - "9222:9222"                   # external DevTools via socat
    expose:
      - "18080"                       # health endpoint (internal)
      - "9221"                        # internal DevTools port
    volumes:
      - /opt/volumes/esp32-rdp/pw-data:/pw-data
    shm_size: 1gb
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost:18080 || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 5
      start_period: 10s

  debug-proxy:
    image: alpine/socat
    container_name: remote-webview-server-debug
    restart: unless-stopped
    network_mode: "service:rwvserver"
    depends_on:
      rwvserver:
        condition: service_healthy
    command:
      - "-d"
      - "-d"
      - "TCP-LISTEN:9222,fork,reuseaddr,keepalive" # external DevTools port
      - "TCP:127.0.0.1:9221"

Can anybody help me ?

Use “main” branch instead of "hardware_jpeg’. It’s 13 commits behind main and does not include any unique changes. I’d consider it outdated.

TBF, I’m not sure if hardware jpeg decoder is really being used in ESP32-P4 at the moment. But for the moment, just focus on getting it stable, if that means reverting to using a software decoder so be it.

Depending on your LCD resolution, you might want to bump up the tile size a bit. Maybe the sheer number of tiles to manage gives the whole solution issues to keep up?

I’ve had issues with setting ‘min_frame_interval’ to anything below 70. But I feel this might related to the screen resolution. My device has a 1280x800 LCD so drawing a whole frame is time intensive that’s for sure. Maybe you can get by with a lower number but start high and move down bit by bit.

Side note, on ESPHome 2026.01.0, I have a bootloop issue when setting cpu_frequency to 400MHz, Ihad to dial it down to 360MHz

1 Like

Thank you for the note. Very helpful! :wink:

1 Like

I see you edited your message but here is a relevant part of my config anyways

substitutions:
  ha_dash_url: http://192.168.0.250:8123/lovelace/hilo

external_components:
  - source: github://strange-v/RemoteWebViewClient@main
    refresh: 0s
    components: [ remote_webview ]

esp32:
  framework:
    components:
      - name: "espressif/esp_websocket_client"
        ref: 1.6.1
      - name: "espressif/esp-dsp"
        ref: 1.7.0
      - name: "bitbank2/jpegdec"
        source: https://github.com/strange-v/jpegdec-esphome
        
remote_webview:
  id: rwv
  server: 192.168.0.250:8081
  url: "${ha_dash_url}"  # set url: "self-test" to initiate the self-test
  device_id: "hmi-10-001"
  tile_size: 128000
  full_frame_tile_count: 4
  full_frame_area_threshold: 0.50
  full_frame_every: 30
  min_frame_interval: 25 #ms
  jpeg_quality: 75
  max_bytes_per_msg: 524288
  big_endian: false
  rotation: 270

text:
  - platform: template
    id: rwv_url
    name: "URL"
    optimistic: true
    restore_value: false
    mode: TEXT
    min_length: 1
    initial_value: "${ha_dash_url}"
    set_action:
      - lambda: |-
          if (!id(rwv).open_url(std::string(x.c_str()))) {
            id(rwv).set_url(std::string(x.c_str()));
            ESP_LOGI("remote_webview", "URL queued (not connected): %s", x.c_str());
          }

I’m still experimenting if using larger tiles results in better refresh rates or not
It does seem to help out with screen tears tho

I’m trying to find the right settings here to maximize performance since I’m confident a ESP32-P4 with hardware JPEG decode and a good wifi (esp32-c6 has wifi 6) can take quite a mouthful. It just feels a bit too sluggish at the moment to be honest.

Oh and you might want to look into updating the firmware of the esp32 coprocessor in charge of wifi.

Documentation:

You need ESPHome 2026.01.0 at least and I found out the update will not start on its own even if the main ESP sees a new firmware

here’s a sample code block I used:

http_request:

update:
  - platform: esp32_hosted
    id: id_esp32_hosted_update
    type: http
    source: https://esphome.github.io/esp-hosted-firmware/manifest/esp32c6.json
    update_interval: 6h

button: 
  - platform: template
    name: Check co-processor firmware
    entity_category: diagnostic
    icon: "mdi:chip"
    on_press:
      - update.check: id_esp32_hosted_update
  - platform: template
    name: Perform co-processor update
    entity_category: diagnostic
    icon: "mdi:chip"
    on_press:
      - update.perform: id_esp32_hosted_update

Enable debug logs, once connected press the check button (might not be required but does not hurt), then the “Perform update” button to apply the firmware update. Your system will reboot once complete.

3 Likes

Thanks for help is working a lot better.

Great config! Thank you for sharing!

Which display are you using?

Guition JC8012P4A1C 10 inch 1280 x 800 with ESP32-P4 and ESP32-C6

Camera is currently not supported in ESPHome but I don’t really mind

I’m not quite satisfied with the performance of the RemoteWebView solution but I feel it’s mostly a configuration issue.

I’ll upload my config on Github in the coming days.

Does anyone have working code for waveshare ESP32-S3-Touch-LCD-4 rev 3.0?
I found this, that seems to be exactly that, but it seems to fail properly setup the screen and ends up with solid yellow color. Maybe something has changed, allthou rev is the same?

This is a tangentally related question. But couldn’t this remote webview approach be used to make underpowered tablets display HA dashboards better?

In many underpowered tablets, the dashboards grind them to a crawl, it seems like this technique could prevent that. I haven’t seen any other projects that offer this.

Hi!
I’m new to HA.
My server is running
Dell Wyse
Home Assistant OS
Core 2026.1.3
I don’t know HOW to run RemoteWebViewServer - where should I start?

Hi, I’m running 7× Guition ESP32-S3 480×480 touch displays with ESPHome + RemoteWebViewClient/RemoteWebViewServer.

Two quick questions + one issue:

  1. What’s the recommended setup for 7 clients — one server instance for all panels (with unique device_id per display), or multiple server instances/ports? Any “known good” server/client settings for stability with many clients?

  2. How do you handle the “first tap” when the backlight is OFF? Right now the first tap wakes the screen but also gets sent into the webview and clicks something unintentionally. Is there a way to ignore/disable touch while sleeping, or is the best practice to switch to a safe/blank “wake” view while the screen is off?

  3. I’m also seeing occasional rendering glitches: e.g. when adjusting brightness, the slider animation sometimes “breaks apart” / tears / doesn’t redraw cleanly. Is there a reco
    ::contentReference[oaicite:0]{index=0}

Btw very good job boys

  1. There is an option to turn off the touch function. I have set it up so that when the display is off, the touch function is also off, and when the display is on, the touch function is reactivated.
    (This only controls the transmission of the touch function.)
light:
  - platform: monochromatic
    output: gpio_backlight_pwm
    name: Display Backlight
    icon: mdi:lightbulb-on
    id: display_backlight
    restore_mode: ALWAYS_OFF
    on_turn_on:
      then:
        - delay: 0.5s
        - lambda: |-
            id(rwv).disable_touch(false);
            
    on_turn_off:
      then:
        - lambda: |-
            id(rwv).disable_touch(true);

Can I display the camera on the dashboard? I’ve set everything up, but when I open any camera, I get a webrtc error. What am I doing wrong? Or is it impossible to view the cameras?

Yes, you can.

The stream must be in MJPEG format.

If you’re using go2rtc, you’ll need to configure it to transcode XXX→MJPEG.
For example,

go2rtc.yaml:

streams:
PTZ_R_SD_MJPEG: #destination name
- rtsp://your_stream_h264 # source
- "ffmpeg:PTZ_R_MJPEG#video=mjpeg#width=480#height=350"

This camera (PTZ_R_SD_MJPEG) is displayed on the screen.

Note:
When viewing a camera CPU usage (transcoding) increases significantly,
and loading the image (starting transcoding) takes several seconds.

Thank you, everything worked. However, after 15 seconds of viewing the “video” on the ESP panel, it reboots.