Last active
June 19, 2026 08:52
-
-
Save hectorzin/25483aac4dc888cddab0302b8d19a039 to your computer and use it in GitHub Desktop.
CrowPanel Advanced 10 1inch ESP32 P4 HMI AI Display ESPHome Examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "CrowPanel Home Assistant WebView" | |
| version: "0.1.5" | |
| slug: "remote_webview_server" | |
| description: "Streams your HA dashboard or any website to ESP32P4 CrowPanel via WebSocket." | |
| url: "https://github.com/fintros/RemoteWebViewServer/tree/main/hassio/remote_webview_server" | |
| arch: | |
| - amd64 | |
| - aarch64 | |
| startup: application | |
| boot: auto | |
| host_network: true | |
| video: true | |
| udev: true | |
| init: false | |
| watchdog: "http://[HOST]:[PORT:18080]/" | |
| map: | |
| - type: addon_config | |
| read_only: false | |
| options: | |
| tile_size: 256 | |
| 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: 122880 | |
| ws_port: 8081 | |
| debug_port: 9221 | |
| expose_debug_proxy: false | |
| debug_proxy_port: 9222 | |
| health_port: 18080 | |
| prefers_reduced_motion: false | |
| inject_js_url: "" | |
| inject_js_allow_http: false | |
| user_data_dir: "/pw-data" | |
| schema: | |
| tile_size: int | |
| full_frame_tile_count: int | |
| full_frame_area_threshold: float | |
| full_frame_every: int | |
| every_nth_frame: int | |
| min_frame_interval_ms: int | |
| jpeg_quality: int | |
| max_bytes_per_message: int | |
| ws_port: int | |
| debug_port: int | |
| expose_debug_proxy: bool | |
| debug_proxy_port: int | |
| health_port: int | |
| prefers_reduced_motion: bool | |
| inject_js_url: str | |
| inject_js_allow_http: bool | |
| user_data_dir: str |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| FROM mcr.microsoft.com/playwright:v1.60.0-jammy | |
| WORKDIR /app | |
| RUN apt-get update \ | |
| && apt-get install -y --no-install-recommends \ | |
| git \ | |
| jq \ | |
| socat \ | |
| ca-certificates \ | |
| curl \ | |
| && rm -rf /var/lib/apt/lists/* | |
| RUN git clone https://github.com/fintros/RemoteWebViewServer.git /src | |
| WORKDIR /src | |
| ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 | |
| ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright | |
| RUN npm ci --no-audit --no-fund | |
| RUN npm run build | |
| RUN npm prune --omit=dev | |
| RUN cp -r dist /app/dist | |
| RUN cp -r node_modules /app/node_modules | |
| WORKDIR /app | |
| COPY run.sh /run.sh | |
| RUN chmod +x /run.sh | |
| EXPOSE 8081 18080 9221 | |
| CMD ["/run.sh"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| OPTIONS_FILE="/data/options.json" | |
| get_opt() { | |
| local key="$1" default="$2" | |
| if [ -f "$OPTIONS_FILE" ]; then | |
| jq -r --arg k "$key" --arg d "$default" '.[$k] // $d' "$OPTIONS_FILE" | |
| else | |
| echo "$default" | |
| fi | |
| } | |
| export TILE_SIZE="$(get_opt tile_size 32)" | |
| export FULL_FRAME_TILE_COUNT="$(get_opt full_frame_tile_count 4)" | |
| export FULL_FRAME_AREA_THRESHOLD="$(get_opt full_frame_area_threshold 0.5)" | |
| export FULL_FRAME_EVERY="$(get_opt full_frame_every 50)" | |
| export EVERY_NTH_FRAME="$(get_opt every_nth_frame 1)" | |
| export MIN_FRAME_INTERVAL_MS="$(get_opt min_frame_interval_ms 80)" | |
| export JPEG_QUALITY="$(get_opt jpeg_quality 85)" | |
| export MAX_BYTES_PER_MESSAGE="$(get_opt max_bytes_per_message 14336)" | |
| export WS_PORT="$(get_opt ws_port 8081)" | |
| export DEBUG_PORT="$(get_opt debug_port 9221)" | |
| export HEALTH_PORT="$(get_opt health_port 18080)" | |
| export PREFERS_REDUCED_MOTION="$(get_opt prefers_reduced_motion false)" | |
| export INJECT_JS_URL="$(get_opt inject_js_url "")" | |
| export INJECT_JS_ALLOW_HTTP="$(get_opt inject_js_allow_http false)" | |
| USER_DATA_DIR_OPT="$(get_opt user_data_dir "/pw-data")" | |
| if [ "$USER_DATA_DIR_OPT" = "/pw-data" ]; then | |
| mkdir -p /data/pw-data | |
| # if not already a symlink, make /pw-data -> /data/pw-data | |
| if [ ! -L /pw-data ]; then | |
| rm -rf /pw-data 2>/dev/null || true | |
| ln -s /data/pw-data /pw-data | |
| fi | |
| export USER_DATA_DIR="/pw-data" | |
| else | |
| mkdir -p "$USER_DATA_DIR_OPT" | |
| export USER_DATA_DIR="$USER_DATA_DIR_OPT" | |
| fi | |
| EXPOSE_DEBUG_PROXY="$(get_opt expose_debug_proxy false)" | |
| if [ "$EXPOSE_DEBUG_PROXY" = "true" ]; then | |
| DEBUG_PROXY_PORT="$(get_opt debug_proxy_port 9222)" | |
| echo "[remote-webview] Starting debug proxy on :${DEBUG_PROXY_PORT} -> 127.0.0.1:${DEBUG_PORT}" | |
| socat -d -d "TCP-LISTEN:${DEBUG_PROXY_PORT},fork,reuseaddr,keepalive" "TCP:127.0.0.1:${DEBUG_PORT}" & | |
| fi | |
| command -v node >/dev/null | |
| test -f dist/index.js | |
| exec node dist/index.js |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| esphome: | |
| name: advance-p4-10-1-inch | |
| friendly_name: Advance-P4-10.1-inch | |
| on_boot: | |
| priority: 800 | |
| then: | |
| - logger.log: "Backlight ON" | |
| - output.turn_on: backlight_pwm | |
| - delay: 200ms | |
| - output.turn_off: power_light | |
| - delay: 500ms | |
| - light.turn_on: | |
| id: back_light | |
| brightness: 1 | |
| esp32: | |
| variant: esp32p4 | |
| engineering_sample: true | |
| cpu_frequency: 360MHZ | |
| flash_size: 16MB | |
| framework: | |
| type: esp-idf | |
| advanced: | |
| execute_from_psram: true | |
| enable_idf_experimental_features: true | |
| psram: | |
| speed: 200MHz | |
| esp_ldo: | |
| - channel: 3 | |
| voltage: 2.5V | |
| - channel: 4 | |
| voltage: 2.5V | |
| # Enable logging | |
| logger: | |
| level: debug | |
| logs: | |
| lvgl: info | |
| hardware_uart: UART0 | |
| # Enable Home Assistant API | |
| api: | |
| encryption: | |
| key: "Dp4Wgl1jj5F++6mnzSma7lHvgHCGfghlE/ohT96S+jw=" | |
| ota: | |
| - platform: esphome | |
| password: "bf1c7324cdcd0f41044c7722cbf5a048" | |
| esp32_hosted: | |
| variant: esp32c6 | |
| active_high: true | |
| reset_pin: GPIO32 | |
| cmd_pin: GPIO19 | |
| clk_pin: GPIO18 | |
| d0_pin: GPIO17 | |
| d1_pin: GPIO16 | |
| d2_pin: GPIO15 | |
| d3_pin: GPIO14 | |
| wifi: | |
| ssid: !secret wifi_ssid | |
| password: !secret wifi_password | |
| # Enable fallback hotspot (captive portal) in case wifi connection fails | |
| ap: | |
| ssid: "Advance-P4-10.1-Inch" | |
| password: "eW0iGlqyJxnC" | |
| captive_portal: | |
| i2c: | |
| - id: bus_a | |
| sda: GPIO45 | |
| scl: GPIO46 | |
| frequency: 400kHz | |
| sensor: | |
| - platform: aht10 | |
| variant: AHT20 | |
| temperature: | |
| name: "Room Temperature" | |
| id: room_temp | |
| unit_of_measurement: "°C" | |
| accuracy_decimals: 1 | |
| humidity: | |
| name: "Room Humidity" | |
| id: room_hum | |
| unit_of_measurement: "%" | |
| accuracy_decimals: 1 | |
| update_interval: 1s | |
| touchscreen: | |
| - platform: gt911 | |
| id: my_touchscreen | |
| i2c_id: bus_a | |
| reset_pin: 40 | |
| interrupt_pin: 42 | |
| update_interval: 50ms | |
| transform: | |
| swap_xy: false | |
| mirror_x: false | |
| mirror_y: false | |
| # Enabled Touch Logger here | |
| on_touch: | |
| - logger.log: | |
| format: "Touch at (%d, %d)" | |
| args: [touch.x, touch.y] | |
| - lambda: |- | |
| ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%d", | |
| touch.x, | |
| touch.y, | |
| touch.x_raw, | |
| touch.y_raw | |
| ); | |
| switch: | |
| - platform: gpio | |
| pin: GPIO48 | |
| name: "LED" | |
| id: led_output | |
| on_turn_on: | |
| # Switch bulb diagram | |
| - lvgl.image.update: | |
| id: lvgl_light | |
| src: light_on | |
| # Switch the status label text to ON | |
| - lvgl.label.update: | |
| id: label_light_state | |
| text: "ON" | |
| on_turn_off: | |
| # Switch bulb diagram | |
| - lvgl.image.update: | |
| id: lvgl_light | |
| src: light_off | |
| # Switch the status label text to OFF | |
| - lvgl.label.update: | |
| id: label_light_state | |
| text: "OFF" | |
| output: | |
| - platform: ledc | |
| pin: GPIO31 | |
| id: backlight_pwm | |
| - platform: gpio | |
| id: power_light | |
| pin: GPIO29 | |
| inverted: true | |
| light: | |
| - platform: monochromatic | |
| name: "Backlight" | |
| output: backlight_pwm | |
| id: back_light | |
| restore_mode: ALWAYS_ON | |
| internal: true | |
| display: | |
| - platform: mipi_dsi | |
| id: my_display | |
| model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B | |
| reset_pin: | |
| number: 41 | |
| update_interval: never | |
| auto_clear_enabled: false | |
| dimensions: | |
| width: 1024 | |
| height: 600 | |
| color_order: RGB | |
| color_depth: 16 | |
| image: | |
| - file: "small_temp.png" | |
| id: small_temp | |
| resize: 200x200 | |
| type: RGB565 | |
| byte_order: little_endian | |
| - file: "small_hum.png" | |
| id: small_hum | |
| resize: 200x200 | |
| type: RGB565 | |
| byte_order: little_endian | |
| - file: "small_logo.png" | |
| id: small_logo | |
| resize: 200x100 | |
| type: RGB565 | |
| byte_order: little_endian | |
| - file: "light.png" | |
| id: light_on | |
| resize: 200x200 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "no_light.png" | |
| id: light_off | |
| resize: 200x200 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| lvgl: | |
| buffer_size: 50% | |
| byte_order: little_endian | |
| color_depth: 16 | |
| log_level: INFO | |
| bg_color: 0xFFFFFF | |
| text_font: montserrat_26 | |
| widgets: | |
| - image: | |
| id: lvgl_logo | |
| src: small_logo | |
| x: 400 | |
| y: 20 | |
| - image: | |
| id: lvgl_temp | |
| src: small_temp | |
| x: 250 | |
| y: 250 | |
| - image: | |
| id: lvgl_hum | |
| src: small_hum | |
| x: 500 | |
| y: 250 | |
| - image: | |
| id: lvgl_light | |
| src: light_off # Default 'Turn off lights' image | |
| x: 750 # You can adjust the position by yourself | |
| y: 250 | |
| - label: | |
| id: label_temp | |
| x: 190 | |
| y: 350 | |
| text: "Temp: --°C" | |
| - label: | |
| id: label_hum | |
| x: 450 | |
| y: 350 | |
| text: "Humi: --%" | |
| - label: | |
| id: label_light_state | |
| x: 750 | |
| y: 350 | |
| text: "OFF" | |
| interval: | |
| - interval: 1s | |
| then: | |
| - lambda: |- | |
| char temp_str[20]; | |
| char hum_str[20]; | |
| snprintf(temp_str, sizeof(temp_str), "Temp: %.1f°C", id(room_temp).state); | |
| snprintf(hum_str, sizeof(hum_str), "Humi: %.1f%%", id(room_hum).state); | |
| lv_label_set_text(id(label_temp), temp_str); | |
| lv_label_set_text(id(label_hum), hum_str); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| esphome: | |
| name: advance-p4-10-1-inch | |
| friendly_name: Advance-P4-10.1-inch | |
| on_boot: | |
| priority: 800 | |
| then: | |
| - logger.log: "Backlight ON" | |
| - output.turn_on: backlight_pwm | |
| - delay: 200ms | |
| - output.turn_off: power_light | |
| - delay: 500ms | |
| - light.turn_on: | |
| id: back_light | |
| brightness: 1 | |
| esp32: | |
| variant: esp32p4 | |
| engineering_sample: true | |
| cpu_frequency: 360MHZ | |
| flash_size: 16MB | |
| framework: | |
| type: esp-idf | |
| advanced: | |
| execute_from_psram: true | |
| enable_idf_experimental_features: true | |
| psram: | |
| speed: 200MHz | |
| esp_ldo: | |
| - channel: 3 | |
| voltage: 2.5V | |
| - channel: 4 | |
| voltage: 2.5V | |
| # Enable logging | |
| logger: | |
| level: debug | |
| logs: | |
| lvgl: info | |
| hardware_uart: UART0 | |
| # Enable Home Assistant API | |
| api: | |
| encryption: | |
| key: "Dp4Wgl1jj5F++6mnzSma7lHvgHCGfghlE/ohT96S+jw=" | |
| ota: | |
| - platform: esphome | |
| password: "bf1c7324cdcd0f41044c7722cbf5a048" | |
| esp32_hosted: | |
| variant: esp32c6 | |
| active_high: true | |
| reset_pin: GPIO32 | |
| cmd_pin: GPIO19 | |
| clk_pin: GPIO18 | |
| d0_pin: GPIO17 | |
| d1_pin: GPIO16 | |
| d2_pin: GPIO15 | |
| d3_pin: GPIO14 | |
| wifi: | |
| ssid: !secret wifi_ssid | |
| password: !secret wifi_password | |
| # Enable fallback hotspot (captive portal) in case wifi connection fails | |
| ap: | |
| ssid: "Advance-P4-10.1-Inch" | |
| password: "eW0iGlqyJxnC" | |
| captive_portal: | |
| i2c: | |
| - id: bus_a | |
| sda: GPIO45 | |
| scl: GPIO46 | |
| frequency: 400kHz | |
| sensor: | |
| - platform: homeassistant | |
| entity_id: sensor.ws2900_v2_02_03_indoor_temperature | |
| id: room_temp | |
| - platform: homeassistant | |
| entity_id: sensor.ws2900_v2_02_03_indoor_humidity | |
| id: room_hum | |
| touchscreen: | |
| - platform: gt911 | |
| id: my_touchscreen | |
| i2c_id: bus_a | |
| reset_pin: 40 | |
| interrupt_pin: 42 | |
| update_interval: 50ms | |
| transform: | |
| swap_xy: false | |
| mirror_x: false | |
| mirror_y: false | |
| # Enabled Touch Logger here | |
| on_touch: | |
| - logger.log: | |
| format: "Touch at (%d, %d)" | |
| args: [touch.x, touch.y] | |
| - lambda: |- | |
| ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%d", | |
| touch.x, | |
| touch.y, | |
| touch.x_raw, | |
| touch.y_raw | |
| ); | |
| switch: | |
| - platform: gpio | |
| pin: GPIO48 | |
| name: "LED" | |
| id: led_output | |
| on_turn_on: | |
| # Switch bulb diagram | |
| - lvgl.image.update: | |
| id: lvgl_light | |
| src: light_on | |
| # Switch the status label text to ON | |
| - lvgl.label.update: | |
| id: label_light_state | |
| text: "ON" | |
| on_turn_off: | |
| # Switch bulb diagram | |
| - lvgl.image.update: | |
| id: lvgl_light | |
| src: light_off | |
| # Switch the status label text to OFF | |
| - lvgl.label.update: | |
| id: label_light_state | |
| text: "OFF" | |
| output: | |
| - platform: ledc | |
| pin: GPIO31 | |
| id: backlight_pwm | |
| - platform: gpio | |
| id: power_light | |
| pin: GPIO29 | |
| inverted: true | |
| light: | |
| - platform: monochromatic | |
| name: "Backlight" | |
| output: backlight_pwm | |
| id: back_light | |
| restore_mode: ALWAYS_ON | |
| internal: true | |
| display: | |
| - platform: mipi_dsi | |
| id: my_display | |
| model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B | |
| reset_pin: | |
| number: 41 | |
| update_interval: never | |
| auto_clear_enabled: false | |
| dimensions: | |
| width: 1024 | |
| height: 600 | |
| color_order: RGB | |
| color_depth: 16 | |
| image: | |
| - file: "small_temp.png" | |
| id: small_temp | |
| resize: 200x200 | |
| type: RGB565 | |
| byte_order: little_endian | |
| - file: "small_hum.png" | |
| id: small_hum | |
| resize: 200x200 | |
| type: RGB565 | |
| byte_order: little_endian | |
| - file: "small_logo.png" | |
| id: small_logo | |
| resize: 200x100 | |
| type: RGB565 | |
| byte_order: little_endian | |
| - file: "light.png" | |
| id: light_on | |
| resize: 200x200 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "no_light.png" | |
| id: light_off | |
| resize: 200x200 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| lvgl: | |
| buffer_size: 50% | |
| byte_order: little_endian | |
| color_depth: 16 | |
| log_level: INFO | |
| bg_color: 0xFFFFFF | |
| text_font: montserrat_26 | |
| widgets: | |
| - image: | |
| id: lvgl_logo | |
| src: small_logo | |
| x: 400 | |
| y: 20 | |
| - image: | |
| id: lvgl_temp | |
| src: small_temp | |
| x: 250 | |
| y: 250 | |
| - image: | |
| id: lvgl_hum | |
| src: small_hum | |
| x: 500 | |
| y: 250 | |
| - image: | |
| id: lvgl_light | |
| src: light_off # Default 'Turn off lights' image | |
| x: 750 # You can adjust the position by yourself | |
| y: 250 | |
| - label: | |
| id: label_temp | |
| x: 190 | |
| y: 350 | |
| text: "Temp: --°C" | |
| - label: | |
| id: label_hum | |
| x: 450 | |
| y: 350 | |
| text: "Humi: --%" | |
| - label: | |
| id: label_light_state | |
| x: 750 | |
| y: 350 | |
| text: "OFF" | |
| interval: | |
| - interval: 1s | |
| then: | |
| - lambda: |- | |
| char temp_str[20]; | |
| char hum_str[20]; | |
| snprintf(temp_str, sizeof(temp_str), "Temp: %.1f°C", id(room_temp).state); | |
| snprintf(hum_str, sizeof(hum_str), "Humi: %.1f%%", id(room_hum).state); | |
| lv_label_set_text(id(label_temp), temp_str); | |
| lv_label_set_text(id(label_hum), hum_str); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| esphome: | |
| name: advance-p4-10-1-inch | |
| friendly_name: Advance-P4-10.1-inch | |
| on_boot: | |
| priority: 800 | |
| then: | |
| - logger.log: "Backlight ON" | |
| - output.turn_on: backlight_pwm | |
| - delay: 200ms | |
| - output.turn_off: power_light | |
| - delay: 500ms | |
| - light.turn_on: | |
| id: back_light | |
| brightness: 1 | |
| esp32: | |
| variant: esp32p4 | |
| engineering_sample: true | |
| cpu_frequency: 360MHZ | |
| flash_size: 16MB | |
| framework: | |
| type: esp-idf | |
| advanced: | |
| execute_from_psram: true | |
| enable_idf_experimental_features: true | |
| psram: | |
| speed: 200MHz | |
| esp_ldo: | |
| - channel: 3 | |
| voltage: 2.5V | |
| - channel: 4 | |
| voltage: 2.5V | |
| # Enable logging | |
| logger: | |
| level: debug | |
| logs: | |
| lvgl: info | |
| hardware_uart: UART0 | |
| # Enable Home Assistant API | |
| api: | |
| encryption: | |
| key: "Dp4Wgl1jj5F++6mnzSma7lHvgHCGfghlE/ohT96S+jw=" | |
| ota: | |
| - platform: esphome | |
| password: "bf1c7324cdcd0f41044c7722cbf5a048" | |
| esp32_hosted: | |
| variant: esp32c6 | |
| active_high: true | |
| reset_pin: GPIO32 | |
| cmd_pin: GPIO19 | |
| clk_pin: GPIO18 | |
| d0_pin: GPIO17 | |
| d1_pin: GPIO16 | |
| d2_pin: GPIO15 | |
| d3_pin: GPIO14 | |
| wifi: | |
| ssid: !secret wifi_ssid | |
| password: !secret wifi_password | |
| # Enable fallback hotspot (captive portal) in case wifi connection fails | |
| ap: | |
| ssid: "Advance-P4-10.1-Inch" | |
| password: "eW0iGlqyJxnC" | |
| captive_portal: | |
| i2c: | |
| - id: bus_a | |
| sda: GPIO45 | |
| scl: GPIO46 | |
| frequency: 400kHz | |
| sensor: | |
| - platform: homeassistant | |
| entity_id: sensor.ws2900_v2_02_03_indoor_temperature | |
| id: room_temp | |
| - platform: homeassistant | |
| entity_id: sensor.ws2900_v2_02_03_indoor_humidity | |
| id: room_hum | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_pm2_5 | |
| id: pm25 | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_pm10 | |
| id: pm10 | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_pm1 | |
| id: pm1 | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_dioxido_de_carbono | |
| id: co2 | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_indice_cov | |
| id: voc | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_indice_nox | |
| id: nox | |
| touchscreen: | |
| - platform: gt911 | |
| id: my_touchscreen | |
| i2c_id: bus_a | |
| reset_pin: 40 | |
| interrupt_pin: 42 | |
| update_interval: 50ms | |
| transform: | |
| swap_xy: false | |
| mirror_x: false | |
| mirror_y: false | |
| # Enabled Touch Logger here | |
| on_touch: | |
| - logger.log: | |
| format: "Touch at (%d, %d)" | |
| args: [touch.x, touch.y] | |
| - lambda: |- | |
| ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%d", | |
| touch.x, | |
| touch.y, | |
| touch.x_raw, | |
| touch.y_raw | |
| ); | |
| switch: | |
| - platform: gpio | |
| pin: GPIO48 | |
| name: "LED" | |
| id: led_output | |
| output: | |
| - platform: ledc | |
| pin: GPIO31 | |
| id: backlight_pwm | |
| - platform: gpio | |
| id: power_light | |
| pin: GPIO29 | |
| inverted: true | |
| light: | |
| - platform: monochromatic | |
| name: "Backlight" | |
| output: backlight_pwm | |
| id: back_light | |
| restore_mode: ALWAYS_ON | |
| internal: true | |
| display: | |
| - platform: mipi_dsi | |
| id: my_display | |
| model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B | |
| reset_pin: | |
| number: 41 | |
| update_interval: never | |
| auto_clear_enabled: false | |
| dimensions: | |
| width: 1024 | |
| height: 600 | |
| color_order: RGB | |
| color_depth: 16 | |
| image: | |
| - file: "temperatura_256.png" | |
| id: icon_temp | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "humedad_256.png" | |
| id: icon_hum | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "co2_256.png" | |
| id: icon_co2 | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "pm25_256.png" | |
| id: icon_pm25 | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "pm10_256.png" | |
| id: icon_pm10 | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "voc_256.png" | |
| id: icon_voc | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "nox_256.png" | |
| id: icon_nox | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| lvgl: | |
| buffer_size: 50% | |
| byte_order: little_endian | |
| color_depth: 16 | |
| log_level: INFO | |
| bg_color: 0xFFFFFF | |
| text_font: montserrat_26 | |
| widgets: | |
| - label: | |
| id: titulo | |
| text: "CALIDAD DEL AIRE INTERIOR" | |
| x: 290 | |
| y: 20 | |
| - label: | |
| id: estado_aire | |
| text: "BUENO" | |
| x: 450 | |
| y: 65 | |
| # FILA 1 | |
| - image: | |
| src: icon_temp | |
| x: 80 | |
| y: 130 | |
| - label: | |
| id: temperatura | |
| text: "--.- C" | |
| x: 40 | |
| y: 240 | |
| - image: | |
| src: icon_hum | |
| x: 320 | |
| y: 130 | |
| - label: | |
| id: humedad | |
| text: "-- %" | |
| x: 300 | |
| y: 240 | |
| - image: | |
| src: icon_co2 | |
| x: 560 | |
| y: 130 | |
| - label: | |
| id: co2_label | |
| text: "--- ppm" | |
| x: 500 | |
| y: 240 | |
| - image: | |
| src: icon_voc | |
| x: 800 | |
| y: 130 | |
| - label: | |
| id: voc_label | |
| text: "---" | |
| x: 820 | |
| y: 240 | |
| # FILA 2 | |
| - image: | |
| src: icon_nox | |
| x: 80 | |
| y: 340 | |
| - label: | |
| id: nox_label | |
| text: "---" | |
| x: 110 | |
| y: 450 | |
| - image: | |
| src: icon_pm25 | |
| x: 320 | |
| y: 340 | |
| - label: | |
| id: pm1_label | |
| text: "PM1 --.-" | |
| x: 270 | |
| y: 450 | |
| - image: | |
| src: icon_pm25 | |
| x: 560 | |
| y: 340 | |
| - label: | |
| id: pm25_label | |
| text: "PM2.5 --.-" | |
| x: 500 | |
| y: 450 | |
| - image: | |
| src: icon_pm10 | |
| x: 800 | |
| y: 340 | |
| - label: | |
| id: pm10_label | |
| text: "PM10 --.-" | |
| x: 740 | |
| y: 450 | |
| interval: | |
| - interval: 1s | |
| then: | |
| - lambda: |- | |
| char txt[64]; | |
| snprintf(txt, sizeof(txt), "Temp: %.1f °C", id(room_temp).state); | |
| lv_label_set_text(id(temperatura), txt); | |
| snprintf(txt, sizeof(txt), "Hum: %.0f %%", id(room_hum).state); | |
| lv_label_set_text(id(humedad), txt); | |
| snprintf(txt, sizeof(txt), "CO2: %.0f ppm", id(co2).state); | |
| lv_label_set_text(id(co2_label), txt); | |
| snprintf(txt, sizeof(txt), "VOC: %.0f", id(voc).state); | |
| lv_label_set_text(id(voc_label), txt); | |
| snprintf(txt, sizeof(txt), "NOx: %.0f", id(nox).state); | |
| lv_label_set_text(id(nox_label), txt); | |
| snprintf(txt, sizeof(txt), "PM1: %.1f", id(pm1).state); | |
| lv_label_set_text(id(pm1_label), txt); | |
| snprintf(txt, sizeof(txt), "PM2.5: %.1f", id(pm25).state); | |
| lv_label_set_text(id(pm25_label), txt); | |
| snprintf(txt, sizeof(txt), "PM10: %.1f", id(pm10).state); | |
| lv_label_set_text(id(pm10_label), txt); | |
| int sev_co2 = | |
| id(co2).state <= 800 ? 0 : | |
| id(co2).state <= 1000 ? 1 : | |
| id(co2).state <= 1400 ? 2 : | |
| id(co2).state <= 2000 ? 3 : 4; | |
| int sev_pm25 = | |
| id(pm25).state <= 10 ? 0 : | |
| id(pm25).state <= 15 ? 1 : | |
| id(pm25).state <= 25 ? 2 : | |
| id(pm25).state <= 35 ? 3 : | |
| id(pm25).state <= 55 ? 4 : 5; | |
| int sev_pm10 = | |
| id(pm10).state <= 20 ? 0 : | |
| id(pm10).state <= 45 ? 2 : | |
| id(pm10).state <= 90 ? 3 : 4; | |
| int sev_voc = | |
| id(voc).state <= 100 ? 0 : | |
| id(voc).state <= 200 ? 2 : | |
| id(voc).state <= 300 ? 3 : 4; | |
| int sev_nox = | |
| id(nox).state <= 50 ? 0 : | |
| id(nox).state <= 100 ? 2 : | |
| id(nox).state <= 200 ? 3 : 4; | |
| int worst = sev_co2; | |
| if (sev_pm25 > worst) worst = sev_pm25; | |
| if (sev_pm10 > worst) worst = sev_pm10; | |
| if (sev_voc > worst) worst = sev_voc; | |
| if (sev_nox > worst) worst = sev_nox; | |
| const char *estado[] = { | |
| "BUENO", | |
| "ACEPTABLE", | |
| "MODERADO", | |
| "REGULAR", | |
| "MALO", | |
| "MUY MALO" | |
| }; | |
| lv_label_set_text(id(estado_aire), estado[worst]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| esphome: | |
| name: advance-p4-10-1-inch | |
| friendly_name: Advance-P4-10.1-inch | |
| on_boot: | |
| priority: 800 | |
| then: | |
| - logger.log: "Backlight ON" | |
| - output.turn_on: backlight_pwm | |
| - delay: 200ms | |
| - output.turn_off: power_light | |
| - delay: 500ms | |
| - light.turn_on: | |
| id: back_light | |
| brightness: 1 | |
| esp32: | |
| variant: esp32p4 | |
| engineering_sample: true | |
| cpu_frequency: 360MHZ | |
| flash_size: 16MB | |
| framework: | |
| type: esp-idf | |
| advanced: | |
| execute_from_psram: true | |
| enable_idf_experimental_features: true | |
| psram: | |
| speed: 200MHz | |
| esp_ldo: | |
| - channel: 3 | |
| voltage: 2.5V | |
| - channel: 4 | |
| voltage: 2.5V | |
| # Enable logging | |
| logger: | |
| level: debug | |
| logs: | |
| lvgl: info | |
| hardware_uart: UART0 | |
| # Enable Home Assistant API | |
| api: | |
| encryption: | |
| key: "Dp4Wgl1jj5F++6mnzSma7lHvgHCGfghlE/ohT96S+jw=" | |
| ota: | |
| - platform: esphome | |
| password: "bf1c7324cdcd0f41044c7722cbf5a048" | |
| esp32_hosted: | |
| variant: esp32c6 | |
| active_high: true | |
| reset_pin: GPIO32 | |
| cmd_pin: GPIO19 | |
| clk_pin: GPIO18 | |
| d0_pin: GPIO17 | |
| d1_pin: GPIO16 | |
| d2_pin: GPIO15 | |
| d3_pin: GPIO14 | |
| wifi: | |
| ssid: !secret wifi_ssid | |
| password: !secret wifi_password | |
| # Enable fallback hotspot (captive portal) in case wifi connection fails | |
| ap: | |
| ssid: "Advance-P4-10.1-Inch" | |
| password: "eW0iGlqyJxnC" | |
| captive_portal: | |
| i2c: | |
| - id: bus_a | |
| sda: GPIO45 | |
| scl: GPIO46 | |
| frequency: 400kHz | |
| sensor: | |
| - platform: homeassistant | |
| entity_id: sensor.ws2900_v2_02_03_indoor_temperature | |
| id: room_temp | |
| - platform: homeassistant | |
| entity_id: sensor.ws2900_v2_02_03_indoor_humidity | |
| id: room_hum | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_pm2_5 | |
| id: pm25 | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_pm10 | |
| id: pm10 | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_pm1 | |
| id: pm1 | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_dioxido_de_carbono | |
| id: co2 | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_indice_cov | |
| id: voc | |
| - platform: homeassistant | |
| entity_id: sensor.i_9psl_indice_nox | |
| id: nox | |
| touchscreen: | |
| - platform: gt911 | |
| id: my_touchscreen | |
| i2c_id: bus_a | |
| reset_pin: 40 | |
| interrupt_pin: 42 | |
| update_interval: 50ms | |
| transform: | |
| swap_xy: false | |
| mirror_x: false | |
| mirror_y: false | |
| # Enabled Touch Logger here | |
| on_touch: | |
| - logger.log: | |
| format: "Touch at (%d, %d)" | |
| args: [touch.x, touch.y] | |
| - lambda: |- | |
| ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%d", | |
| touch.x, | |
| touch.y, | |
| touch.x_raw, | |
| touch.y_raw | |
| ); | |
| switch: | |
| - platform: gpio | |
| pin: GPIO48 | |
| name: "LED" | |
| id: led_output | |
| output: | |
| - platform: ledc | |
| pin: GPIO31 | |
| id: backlight_pwm | |
| - platform: gpio | |
| id: power_light | |
| pin: GPIO29 | |
| inverted: true | |
| light: | |
| - platform: monochromatic | |
| name: "Backlight" | |
| output: backlight_pwm | |
| id: back_light | |
| restore_mode: ALWAYS_ON | |
| internal: true | |
| display: | |
| - platform: mipi_dsi | |
| id: my_display | |
| model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B | |
| reset_pin: | |
| number: 41 | |
| update_interval: never | |
| auto_clear_enabled: false | |
| dimensions: | |
| width: 1024 | |
| height: 600 | |
| color_order: RGB | |
| color_depth: 16 | |
| image: | |
| - file: "temperatura_256.png" | |
| id: icon_temp | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "humedad_256.png" | |
| id: icon_hum | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "co2_256.png" | |
| id: icon_co2 | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "pm25_256.png" | |
| id: icon_pm25 | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "pm10_256.png" | |
| id: icon_pm10 | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "pm1_256.png" | |
| id: icon_pm1 | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "voc_256.png" | |
| id: icon_voc | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| - file: "nox_256.png" | |
| id: icon_nox | |
| resize: 96x96 | |
| type: RGB565 | |
| transparency: alpha_channel | |
| byte_order: little_endian | |
| lvgl: | |
| buffer_size: 50% | |
| byte_order: little_endian | |
| color_depth: 16 | |
| log_level: INFO | |
| bg_color: 0xFFFFFF | |
| text_font: montserrat_26 | |
| pages: | |
| - id: page_sensores | |
| widgets: | |
| - label: | |
| id: titulo | |
| text: "CALIDAD DEL AIRE INTERIOR" | |
| x: 290 | |
| y: 20 | |
| - label: | |
| id: estado_aire | |
| text: "BUENO" | |
| x: 450 | |
| y: 65 | |
| - image: | |
| src: icon_temp | |
| x: 80 | |
| y: 130 | |
| - label: | |
| id: temperatura | |
| text: "--.- C" | |
| x: 40 | |
| y: 240 | |
| - image: | |
| src: icon_hum | |
| x: 320 | |
| y: 130 | |
| - label: | |
| id: humedad | |
| text: "-- %" | |
| x: 300 | |
| y: 240 | |
| - image: | |
| src: icon_co2 | |
| x: 560 | |
| y: 130 | |
| - label: | |
| id: co2_label | |
| text: "--- ppm" | |
| x: 500 | |
| y: 240 | |
| - image: | |
| src: icon_voc | |
| x: 800 | |
| y: 130 | |
| - label: | |
| id: voc_label | |
| text: "---" | |
| x: 820 | |
| y: 240 | |
| - image: | |
| src: icon_nox | |
| x: 80 | |
| y: 340 | |
| - label: | |
| id: nox_label | |
| text: "---" | |
| x: 110 | |
| y: 450 | |
| - image: | |
| src: icon_pm1 | |
| x: 320 | |
| y: 340 | |
| - label: | |
| id: pm1_label | |
| text: "PM1 --.-" | |
| x: 270 | |
| y: 450 | |
| - image: | |
| src: icon_pm25 | |
| x: 560 | |
| y: 340 | |
| - label: | |
| id: pm25_label | |
| text: "PM2.5 --.-" | |
| x: 500 | |
| y: 450 | |
| - image: | |
| src: icon_pm10 | |
| x: 800 | |
| y: 340 | |
| - label: | |
| id: pm10_label | |
| text: "PM10 --.-" | |
| x: 740 | |
| y: 450 | |
| - id: page_notificacion | |
| bg_color: 0x000000 | |
| widgets: | |
| - label: | |
| id: notif_text | |
| text: "NOTIFICACION" | |
| x: 180 | |
| y: 210 | |
| text_font: montserrat_48 | |
| text_color: 0xFF0000 | |
| text: | |
| - platform: template | |
| name: "Mensaje CrowPanel" | |
| id: mensaje_crowpanel | |
| optimistic: true | |
| mode: text | |
| on_value: | |
| then: | |
| - lvgl.page.show: page_notificacion | |
| - lambda: |- | |
| lv_label_set_text(id(notif_text), x.c_str()); | |
| lv_obj_set_x(id(notif_text), 1024); | |
| static lv_anim_t anim; | |
| lv_anim_init(&anim); | |
| lv_anim_set_var(&anim, id(notif_text)); | |
| lv_anim_set_values(&anim, 1024, -1400); | |
| lv_anim_set_time(&anim, 12000); | |
| lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t) lv_obj_set_x); | |
| lv_anim_start(&anim); | |
| - delay: 15s | |
| - lvgl.page.show: page_sensores | |
| interval: | |
| - interval: 1s | |
| then: | |
| - lambda: |- | |
| char txt[64]; | |
| snprintf(txt, sizeof(txt), "Temp: %.1f °C", id(room_temp).state); | |
| lv_label_set_text(id(temperatura), txt); | |
| snprintf(txt, sizeof(txt), "Hum: %.0f %%", id(room_hum).state); | |
| lv_label_set_text(id(humedad), txt); | |
| snprintf(txt, sizeof(txt), "CO2: %.0f ppm", id(co2).state); | |
| lv_label_set_text(id(co2_label), txt); | |
| snprintf(txt, sizeof(txt), "VOC: %.0f", id(voc).state); | |
| lv_label_set_text(id(voc_label), txt); | |
| snprintf(txt, sizeof(txt), "NOx: %.0f", id(nox).state); | |
| lv_label_set_text(id(nox_label), txt); | |
| snprintf(txt, sizeof(txt), "PM1: %.1f", id(pm1).state); | |
| lv_label_set_text(id(pm1_label), txt); | |
| snprintf(txt, sizeof(txt), "PM2.5: %.1f", id(pm25).state); | |
| lv_label_set_text(id(pm25_label), txt); | |
| snprintf(txt, sizeof(txt), "PM10: %.1f", id(pm10).state); | |
| lv_label_set_text(id(pm10_label), txt); | |
| int sev_co2 = | |
| id(co2).state <= 800 ? 0 : | |
| id(co2).state <= 1000 ? 1 : | |
| id(co2).state <= 1400 ? 2 : | |
| id(co2).state <= 2000 ? 3 : 4; | |
| int sev_pm25 = | |
| id(pm25).state <= 10 ? 0 : | |
| id(pm25).state <= 15 ? 1 : | |
| id(pm25).state <= 25 ? 2 : | |
| id(pm25).state <= 35 ? 3 : | |
| id(pm25).state <= 55 ? 4 : 5; | |
| int sev_pm10 = | |
| id(pm10).state <= 20 ? 0 : | |
| id(pm10).state <= 45 ? 2 : | |
| id(pm10).state <= 90 ? 3 : 4; | |
| int sev_voc = | |
| id(voc).state <= 100 ? 0 : | |
| id(voc).state <= 200 ? 2 : | |
| id(voc).state <= 300 ? 3 : 4; | |
| int sev_nox = | |
| id(nox).state <= 50 ? 0 : | |
| id(nox).state <= 100 ? 2 : | |
| id(nox).state <= 200 ? 3 : 4; | |
| int worst = sev_co2; | |
| if (sev_pm25 > worst) worst = sev_pm25; | |
| if (sev_pm10 > worst) worst = sev_pm10; | |
| if (sev_voc > worst) worst = sev_voc; | |
| if (sev_nox > worst) worst = sev_nox; | |
| const char *estado[] = { | |
| "BUENO", | |
| "ACEPTABLE", | |
| "MODERADO", | |
| "REGULAR", | |
| "MALO", | |
| "MUY MALO" | |
| }; | |
| lv_label_set_text(id(estado_aire), estado[worst]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| substitutions: | |
| # Predefined Wi‑Fi access point SSID | |
| wifi_ap: !secret wifi_ssid | |
| # Predefined Wi‑Fi access point password | |
| wifi_password: !secret wifi_password | |
| # Fallback AP SSID used when main Wi‑Fi is not available | |
| fallback_ap: "crowpanel-esp32-p4" | |
| # Fallback AP password | |
| fallback_ap_password: "CHANGE_ME" | |
| # Default base URL for Home Assistant dashboards (used by Remote WebView) | |
| ha_base_url: "http://192.168.1.30:8122/" | |
| # Home Assistant API encryption key for this ESPHome device | |
| ha_encryption_key: "Dp4Wgl1jj5F++6mnzSma7lHvgHCGfghlE/ohT96S+jw=" | |
| esphome: | |
| name: advance-p4-10-1-inch | |
| on_boot: | |
| # Run after most components are initialized | |
| priority: -100 | |
| then: | |
| # Start inactivity timer (controls backlight & touch lock) | |
| - script.execute: inactivity_timer | |
| # Give network and API some time to come up | |
| - delay: 3s | |
| # Re‑apply last saved Remote WebView URL on boot | |
| - lambda: |- | |
| std::string full = id(rwv_url).state.c_str(); | |
| if (full.empty()) return; | |
| // Strip scheme (http:// or https://) | |
| std::string tmp = full; | |
| auto pos = tmp.find("://"); | |
| if (pos != std::string::npos) tmp = tmp.substr(pos + 3); | |
| // Extract host[:port] from host[:port]/path | |
| std::string host_port; | |
| auto slash = tmp.find('/'); | |
| host_port = (slash != std::string::npos) ? tmp.substr(0, slash) : tmp; | |
| // Extract host only (drop port if present) | |
| std::string host_only; | |
| auto colon = host_port.find(':'); | |
| host_only = (colon != std::string::npos) ? host_port.substr(0, colon) : host_port; | |
| // Use host from URL, but fixed Remote WebView port 8082 | |
| if (!host_only.empty()) { | |
| id(rwv).set_server(host_only + ":8082"); | |
| } | |
| // Set the target URL for Remote WebView | |
| id(rwv).set_url(full); | |
| external_components: | |
| # Local custom components directory (Remote WebView implementation) | |
| - source: components | |
| refresh: 0s | |
| components: [ remote_webview ] | |
| esp32: | |
| board: esp32-p4 | |
| framework: | |
| type: esp-idf | |
| # Enable PSRAM usage for code and read‑only data | |
| sdkconfig_options: | |
| CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y | |
| CONFIG_SPIRAM_RODATA: y | |
| # Extra IDF components used by Remote WebView | |
| components: | |
| - name: "espressif/esp_websocket_client" | |
| ref: 1.5.0 | |
| - name: "bitbank2/jpegdec" | |
| source: https://github.com/strange-v/jpegdec-esphome | |
| # Serial logging configuration | |
| logger: | |
| baud_rate: 115200 | |
| hardware_uart: UART0 # Use UART0 for logs | |
| level: INFO | |
| # Native API for Home Assistant | |
| api: | |
| encryption: | |
| key: $ha_encryption_key | |
| # OTA updates over ESPHome | |
| ota: | |
| - platform: esphome | |
| password: "bf1c7324cdcd0f41044c7722cbf5a048" | |
| # ESP32‑Hosted Wi‑Fi6 module connection (ESP32‑C6 on the Waveshare board) | |
| esp32_hosted: | |
| variant: esp32c6 | |
| active_high: true | |
| reset_pin: GPIO32 | |
| cmd_pin: GPIO19 | |
| clk_pin: GPIO18 | |
| d0_pin: GPIO17 | |
| d1_pin: GPIO16 | |
| d2_pin: GPIO15 | |
| d3_pin: GPIO14 | |
| wifi: | |
| # Main Wi‑Fi credentials from substitutions | |
| ssid: $wifi_ap | |
| password: $wifi_password | |
| # Fallback AP (captive portal) when Wi‑Fi cannot connect | |
| ap: | |
| ssid: $fallback_ap | |
| password: $fallback_ap_password | |
| # Captive portal for initial configuration | |
| captive_portal: | |
| # PSRAM configuration (external RAM on ESP32‑P4) | |
| psram: | |
| mode: hex | |
| speed: 200MHz | |
| # Internal LDO regulator used to power some peripherals (e.g. display/touch) | |
| esp_ldo: | |
| - channel: 3 | |
| voltage: 2.5V | |
| switch: | |
| # Controls panel power for LCD and touch | |
| - platform: gpio | |
| pin: GPIO29 | |
| id: display_power | |
| name: "Display Power" | |
| restore_mode: ALWAYS_ON | |
| # Amplifier power (active‑low on this board) | |
| - platform: gpio | |
| id: amp_power | |
| name: "Speaker Power" | |
| pin: | |
| number: GPIO30 | |
| inverted: true | |
| restore_mode: ALWAYS_ON | |
| # Microphone routing switch (R/L channel selection on the board) | |
| - platform: gpio | |
| pin: GPIO20 | |
| id: mic_r_l | |
| name: "Mic R-L" | |
| restore_mode: ALWAYS_ON | |
| output: | |
| # LEDC PWM output for LCD backlight control | |
| - platform: ledc | |
| pin: GPIO31 | |
| id: backlight_pwm | |
| frequency: 1000 Hz | |
| light: | |
| # Simple dimmable backlight light entity | |
| - platform: monochromatic | |
| output: backlight_pwm | |
| name: "Display Backlight" | |
| id: back_light | |
| restore_mode: ALWAYS_ON | |
| display: | |
| # MIPI‑DSI display driver for Waveshare ESP32‑P4 7" LCD | |
| - platform: mipi_dsi | |
| id: my_display | |
| model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B | |
| reset_pin: | |
| number: 41 | |
| rotation: 0 | |
| # Drawing controlled manually by Remote WebView | |
| update_interval: never | |
| auto_clear_enabled: false | |
| dimensions: | |
| width: 1024 | |
| height: 600 | |
| i2c: | |
| # I²C bus for touch controller and other peripherals | |
| - id: bus_a | |
| sda: GPIO45 | |
| scl: GPIO46 | |
| frequency: 400kHz | |
| touchscreen: | |
| # GT911 capacitive touch controller on the Waveshare panel | |
| platform: gt911 | |
| reset_pin: GPIO40 | |
| interrupt_pin: 42 | |
| transform: | |
| mirror_x: false | |
| mirror_y: false | |
| # Any touch event wakes the backlight / restarts inactivity timer | |
| on_touch: | |
| then: | |
| - script.execute: inactivity_timer | |
| remote_webview: | |
| # Remote WebView client that streams HA dashboard to the LCD | |
| id: rwv | |
| # Initial dummy server; will be replaced from rwv_url on boot | |
| server: 127.0.0.1:8082 | |
| tile_size: 128 | |
| # Initial dummy URL; will be replaced from rwv_url on boot | |
| url: http://127.0.0.1/ | |
| full_frame_tile_count: 40 | |
| max_bytes_per_msg: 32768 | |
| jpeg_quality: 85 | |
| every_nth_frame: 3 # el servidor procesa 1 de cada 2 frames | |
| min_frame_interval: 150 # mínimo 150ms entre frames (~6fps) | |
| i2s_audio: | |
| # I2S bus for PDM microphone (clock only, data is on i2s_din_pin) | |
| - id: i2s_mic | |
| i2s_lrclk_pin: GPIO24 | |
| # I2S bus for external speaker amplifier | |
| - id: i2s_spk | |
| i2s_lrclk_pin: GPIO21 | |
| i2s_bclk_pin: GPIO22 | |
| microphone: | |
| # PDM digital microphone connected via I2S | |
| - platform: i2s_audio | |
| id: pdm_mic | |
| i2s_audio_id: i2s_mic | |
| i2s_din_pin: GPIO26 | |
| adc_type: external | |
| pdm: true | |
| sample_rate: 16000 | |
| bits_per_sample: 16bit | |
| speaker: | |
| # External NS4168 I2S speaker amplifier | |
| - platform: i2s_audio | |
| id: ns4168_speaker | |
| i2s_audio_id: i2s_spk | |
| i2s_dout_pin: GPIO23 | |
| dac_type: external | |
| sample_rate: 44100 | |
| bits_per_sample: 16bit | |
| media_player: | |
| # Media player abstraction on top of the I2S speaker | |
| - platform: speaker | |
| id: speaker_media | |
| name: "Speaker" | |
| announcement_pipeline: | |
| speaker: ns4168_speaker | |
| # If an announcement starts while mic is capturing, stop wake word engine | |
| on_announcement: | |
| - if: | |
| condition: | |
| - microphone.is_capturing: | |
| then: | |
| - script.execute: stop_wake_word | |
| # When idle after announcement, restart wake word engine | |
| on_idle: | |
| - script.execute: start_wake_word | |
| micro_wake_word: | |
| # On‑device wake word detection using ESPHome micro_wake_word | |
| on_wake_word_detected: | |
| - logger.log: "Wake word detected" | |
| - script.execute: inactivity_timer | |
| - voice_assistant.start: | |
| wake_word: !lambda return wake_word; | |
| # Voice Activity Detection to suppress noise‑only events | |
| vad: | |
| model: vad | |
| probability_cutoff: 0.85 | |
| sliding_window_size: 2 | |
| # Wake word models running locally | |
| models: | |
| - model: hey_jarvis | |
| probability_cutoff: 0.8 | |
| sliding_window_size: 5 | |
| - model: okay_nabu | |
| sliding_window_size: 5 | |
| probability_cutoff: 0.8 | |
| voice_assistant: | |
| # ESPHome voice assistant integration with Home Assistant Assist | |
| id: va | |
| micro_wake_word: # bind to micro_wake_word component above | |
| microphone: | |
| microphone: pdm_mic | |
| channels: 0 | |
| gain_factor: 1 | |
| media_player: speaker_media | |
| noise_suppression_level: 2 | |
| # auto_gain: 31dBFS | |
| on_end: | |
| # Handle "nevermind" case with no TTS announcement | |
| - wait_until: | |
| condition: | |
| - media_player.is_announcing: | |
| timeout: 0.5s | |
| # Restart only micro_wake_word if enabled; streaming wake words restart automatically | |
| - if: | |
| condition: | |
| - lambda: |- | |
| return id(wake_word_engine_location).current_option() == "On device"; | |
| then: | |
| - wait_until: | |
| - and: | |
| - not: | |
| voice_assistant.is_running: | |
| - not: | |
| speaker.is_playing: | |
| - lambda: id(va).set_use_wake_word(false); | |
| - micro_wake_word.start: | |
| on_client_connected: | |
| # Delay a bit so HA API is fully ready, then start wake word engine | |
| - delay: 2s | |
| - script.execute: start_wake_word | |
| on_client_disconnected: | |
| # Stop wake word when HA disconnects | |
| - script.execute: stop_wake_word | |
| button: | |
| - platform: template | |
| name: "Test Voice" | |
| on_press: | |
| - voice_assistant.start_continuous | |
| text: | |
| # Text entity used to configure Remote WebView URL from HA | |
| - platform: template | |
| id: rwv_url | |
| name: "Remote WebView URL" | |
| optimistic: true | |
| restore_value: true # persist last URL locally in flash | |
| mode: TEXT | |
| min_length: 1 | |
| initial_value: $ha_base_url # default value if nothing stored yet | |
| set_action: | |
| - lambda: |- | |
| std::string full = std::string(x.c_str()); | |
| // Strip scheme | |
| std::string tmp = full; | |
| auto pos = tmp.find("://"); | |
| if (pos != std::string::npos) { | |
| tmp = tmp.substr(pos + 3); | |
| } | |
| // Extract host[:port] | |
| std::string host_only; | |
| auto slash = tmp.find('/'); | |
| std::string host_port = (slash != std::string::npos) ? tmp.substr(0, slash) : tmp; | |
| // Drop port from HA URL, keep only hostname | |
| auto colon = host_port.find(':'); | |
| if (colon != std::string::npos) { | |
| host_only = host_port.substr(0, colon); | |
| } else { | |
| host_only = host_port; | |
| } | |
| // Configure Remote WebView server as <host>:8082 | |
| if (!host_only.empty()) { | |
| std::string server = host_only + ":8082"; | |
| id(rwv).set_server(server); | |
| ESP_LOGI("remote_webview", "Server set to: %s", server.c_str()); | |
| } else { | |
| ESP_LOGW("remote_webview", "Failed to parse host from URL: %s", full.c_str()); | |
| } | |
| // Apply URL to Remote WebView (immediately if connected, else queue) | |
| if (!id(rwv).open_url(full)) { | |
| id(rwv).set_url(full); | |
| ESP_LOGI("remote_webview", "URL queued (not connected): %s", full.c_str()); | |
| } | |
| select: | |
| # Runtime switch: where wake word is handled (HA or on‑device) | |
| - platform: template | |
| entity_category: config | |
| name: Wake word engine location | |
| id: wake_word_engine_location | |
| optimistic: true | |
| restore_value: true | |
| options: | |
| - In Home Assistant | |
| - On device | |
| initial_option: In Home Assistant | |
| on_value: | |
| # Use streaming wake word in HA | |
| - if: | |
| condition: | |
| lambda: return x == "In Home Assistant"; | |
| then: | |
| - micro_wake_word.stop: | |
| - delay: 500ms | |
| - lambda: id(va).set_use_wake_word(true); | |
| - voice_assistant.start_continuous: | |
| # Use on‑device micro_wake_word | |
| - if: | |
| condition: | |
| lambda: return x == "On device"; | |
| then: | |
| - lambda: id(va).set_use_wake_word(false); | |
| - voice_assistant.stop: | |
| - delay: 500ms | |
| - micro_wake_word.start: | |
| script: | |
| # Helper to start appropriate wake word engine depending on select state | |
| - id: start_wake_word | |
| then: | |
| - if: | |
| condition: | |
| and: | |
| - not: | |
| - voice_assistant.is_running: | |
| - lambda: |- | |
| return id(wake_word_engine_location).current_option() == "On device"; | |
| then: | |
| - lambda: id(va).set_use_wake_word(false); | |
| - micro_wake_word.start: | |
| - if: | |
| condition: | |
| and: | |
| - not: | |
| - voice_assistant.is_running: | |
| - lambda: |- | |
| return id(wake_word_engine_location).current_option() == "In Home Assistant"; | |
| then: | |
| - lambda: id(va).set_use_wake_word(true); | |
| - voice_assistant.start: | |
| # Helper to stop wake word engine depending on where it runs | |
| - id: stop_wake_word | |
| then: | |
| - if: | |
| condition: | |
| lambda: |- | |
| return id(wake_word_engine_location).current_option() == "In Home Assistant"; | |
| then: | |
| - lambda: id(va).set_use_wake_word(false); | |
| - voice_assistant.stop: | |
| - if: | |
| condition: | |
| lambda: |- | |
| return id(wake_word_engine_location).current_option() == "On device"; | |
| then: | |
| - micro_wake_word.stop: | |
| # Backlight / touch inactivity timer: | |
| # - wakes display & enables touch on any activity | |
| # - turns display off and disables touch after timeout | |
| - id: inactivity_timer | |
| mode: restart | |
| then: | |
| # If backlight is currently off, turn it on and re‑enable touch | |
| - if: | |
| condition: | |
| light.is_off: back_light | |
| then: | |
| - light.turn_on: | |
| id: back_light | |
| - delay: 0.5s | |
| - lambda: |- | |
| id(rwv).disable_touch(false); | |
| # Wait for inactivity duration | |
| - delay: 15s | |
| # Turn off backlight | |
| - light.turn_off: | |
| id: back_light | |
| # Disable touch so screen does not react while "sleeping" | |
| - lambda: |- | |
| id(rwv).disable_touch(true); | |
| # Example of LVGL usage; if LVGL is enabled, Remote WebView cannot be used | |
| # lvgl: | |
| # buffer_size: 100% | |
| # byte_order: little_endian | |
| # | |
| # pages: | |
| # - id: main_page | |
| # widgets: | |
| # - label: | |
| # id: hello_label | |
| # text: "Hello LVGL" | |
| # align: CENTER |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment