New boards Wishlist

Support new device: Hosyond 4" ESP Screen

afyounieSuggested this wish ·

Per the response on the Home Assistant forum, here is the configuration for the Hosyond board. This is just for the 320x480 board, but it can easily be modified to support the other sized boards as well, assuming they share the same pin configuration.

Amazon listing for reference: https://www.amazon.com/Hosyond-320x480-ESP32-32E-Display-ST7796S/dp/B0FGJJ24S1

packages/boards/hosyond-esp32-4in.yaml | Click to expand
############################################################
# ESP Screens - Hosyond CYD board (ESP32-32E, "Cheap Yellow Display")
# Built-in ST7796 320x480 display and XPT2046 resistive touch.
#
# What is in here: the hardware (display, touch, backlight, LED), the sizes the
# shared tree takes from this board, the code the shared lambdas take from this
# board (hooks), and what only a CYD has: the calibration wizard for its
# resistive glass and a page swipe by LVGL's gesture. Everything else (the LVGL
# tree, the cards, the scripts, the entities) is packages/core.yaml, which every
# board shares; docs/PROFILES.md says how the files fit together.
#
# Entry file include this: packages/hosyond.yaml (the package a screen builds from over GitHub).
############################################################
# The cards of this board's grid: one per cell, from the file for 6 cells
# (tools/generate_cells.py writes them; docs/RESPONSIVE.md says why).
packages:
  cells: !include ../cells/6.yaml

substitutions:
  # A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
  TOUCH_MOVE_LIMIT_PX: "56"
  # Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
  TOUCH_MIN_PRESS_MS: "60"
  # A slider let go this close to the left or right edge of the glass meant its end when that end lies in
  # the band. The resistive panel reports a finger up to the bezel, so only the strip's last pixels and
  # the margin beside them count (a capacitive Guition loses a fast finger 30-50 px before its edge).
  SLIDER_EDGE_SNAP_PX: "20"
  # The key of this board file; the firmware reports it so ESP Screens knows what board a screen is.
  BOARD_ID: "cyd"
  # This board's backlight takes levels (a PWM pin), so brightness is a percentage.
  BACKLIGHT_DIMMABLE: "true"
  # The screen can go dark: standby and night dim it, and Sleep and Wake exist.
  CAN_STANDBY: "true"
  DEVICE_NAME: "cyd-new"
  DEVICE_FRIENDLY_NAME: "My CYD"
  TOUCH_AFFINE_XX: "1.000000000"
  TOUCH_AFFINE_XY: "0.000000000"
  TOUCH_AFFINE_XC: "0.000000000"
  TOUCH_AFFINE_YX: "0.000000000"
  TOUCH_AFFINE_YY: "1.000000000"
  TOUCH_AFFINE_YC: "0.000000000"

  # ORIENTATION
  # The panel's own pixels. The display block below always drives it at this size; LVGL turns the picture and
  # the touch together, so nothing else on this board knows which way the glass hangs.
  PANEL_W: "480"
  PANEL_H: "320"
  # The LVGL angle that lays this panel out lying down (320 x 480). Standing up is a quarter further.
  ROTATION_LANDSCAPE: "90"
  # Which way this screen hangs. ESP Screens writes it when the screen is built, the way it writes the language,
  # and it is the only line that differs between a screen lying down and the same screen standing up.
  LVGL_ROTATION: "${ROTATION_LANDSCAPE}"
  TOUCH_SWAP_XY: "false"
  # true on this board (resistive XPT2046) — was false on the 032c board
  # (capacitive GT911). This is the original upstream value for this panel.
  TOUCH_MIRROR_X: "false"
  TOUCH_MIRROR_Y: "false"
  TOUCH_CAL_X_MIN: "200"
  TOUCH_CAL_X_MAX: "3900"
  TOUCH_CAL_Y_MIN: "200"
  TOUCH_CAL_Y_MAX: "3900"
  # The tile grid: columns and rows of cells, the side margin and the gaps between cells. The shared tree places
  # every cell from these (runtime_tiles::place_page), so a board with more room states more columns or rows.
  GRID_COLS: "2"
  GRID_ROWS: "3"
  # The cells of a page when this screen stands up (firmware 0.2.92+).
  # The 2.8-inch glass is 43 x 57 mm standing up: one column of four cards, each 39 x 10 mm.
  GRID_COLS_PORTRAIT: "1"
  GRID_ROWS_PORTRAIT: "4"
  GRID_MARGIN: "9"
  GRID_GAP_X: "8"
  GRID_GAP_Y: "4"
  DISPLAY_DPI: "143"      # diagonal pixels / diagonal inches; the look's reference density on this board
  LOOK: "compact"
  SCROLL_Y: "40"
  HEADER_Y: "14"
  # Holding the top bar opens the settings page (0.2.44+). The whole strip counts on this board;
  # a finger that travels further than the drift limit of a tap is swiping, not holding.
  SETTINGS_HOLD_X: "0"
  SETTINGS_HOLD_DRIFT_PX: "24"
  ALERT_CARD_W: "292"
  ALERT_CARD_H: "196"
  ALERT_ICON_X: "18"
  ALERT_ICON_Y: "16"
  ALERT_TEXT_X: "62"
  ALERT_TEXT_W: "212"
  ALERT_TITLE_Y: "16"
  ALERT_TITLE_H: "21"         # one line of headline; a DOT label only ends in an ellipsis with a height
  ALERT_SUBTITLE_Y: "44"
  ALERT_SUBTITLE_H: "88"
  ALERT_BUTTON_W: "100"
  ALERT_BUTTON_H: "40"
  ALERT_BUTTON_INSET: "14"
  ALERT_TITLE_MAX: "48"       # bytes; the label ends one line with an ellipsis
  ALERT_BUTTON_MAX: "12"      # bytes on the OK button
  ALERT_SUBTITLE_MAX: "160"   # bytes; wraps over the rows the card has

  FONT_TIME_SIZE: "18"                          # the clock in the top bar
  FONT_HEADLINE_SIZE: "18"                      # card titles
  FONT_WATCH_VALUE_SIZE: "22"
  FONT_CLOCK_DIGITS_SIZE: "24"
  FONT_SETPOINT_SIZE: "40"                      # the climate card's setpoint
  FONT_LABEL_SIZE: "11"                         # tile titles
  FONT_SUBLABEL_SIZE: "11"                      # tile values and small text
  FONT_SUBLABEL_BIG_SIZE: "14"                  # the top bar and settings rows
  FONT_ICON_SIZE: "28"                          # tile icons
  FONT_ICON_MINI_SIZE: "18"                     # the top bar and card keys
  FONT_ICON_HOME_SIZE: "25"          # the home key in the top bar
  FONT_ICON_BIG_SIZE: "40"                      # full-page tiles
  FONT_WATCH_ICON_SIZE: "12"

  # Tiles and the top bar
  TILE_RADIUS: "18"                             # corner of a tile
  TILE_PAD: "8"                                 # inside a tile
  TILE_ICON_SIZE: "36"                          # the icon circle
  HEADER_INSET: "11"                            # the room name and the clock from the glass edge

  # The page bar under the tiles
  PAGE_BAR_H: "36"                              # the page bar
  PAGE_NUMBER_W: "104"                          # the dots in the middle
  PAGE_CHEVRON_INSET: "14"                      # the chevrons from the outer edge

  # Cards: the round keys and the brightness card
  CARD_KEY_X: "10"                              # a card's round key from the side
  CARD_KEY_Y: "8"                               # from the top
  CARD_KEY_SIZE: "40"                           # its diameter
  COLOR_TITLE_W: "210"                          # the colour card's title
  COLOR_TITLE_Y: "21"



  # The touch test page
  TOUCH_TEST_PAGE_PAINT: "paint_calibration"    # the page
  TOUCH_TEST_TITLE: "calibration_title"            # the words at the top
  TOUCH_TEST_TITLE_FONT: "sublabel"
  TOUCH_TEST_TITLE_Y: "65"
  TOUCH_TEST_INSET: "10"                        # the corner crosses from the edge
  TOUCH_TEST_SIZE: "20"                         # a cross
  TOUCH_TEST_PAINT: "paint_calibration"         # the crosses

  # What the shared scripts do differently on this board
  LVGL_DEFAULT_FONT: "sublabel_big"             # LVGL's default font: every label names its own, so this only keeps Montserrat out of the build
  LVGL_BUFFER_SIZE: "12%"                       # LVGL's draw buffer as a share of the screen
  PAGE_DOTS_LARGE: "false"                      # bigger page dots
  EFFECTS_ROW_FONT: "sublabel_big"              # the effects page's rows
  STANDBY_DIM_MS: "300"                         # how long the backlight takes to dim into standby (waking takes 300)
  ALERT_FLASH_FADE_MS: "0"                      # an alert's flash: the fade of each step
  ALERT_FLASH_DARK_MS: "120"                    # how long it stays dark
  ALERT_FLASH_LIT_MS: "170"                     # how long it stays lit

  # Code the shared lambdas of packages/core.yaml take from this board, at the place named ${...}: the
  # exact lines the old profile had there. Empty when this board adds nothing at that place; every board
  # must name every hook, or ESPHome stops at the undefined one.
  # the first boot step: the touch guard, and what this board's panel needs
  BOOT_TOUCH: |-
    // Per-panel affine correction; shared defaults are neutral.
    id(ts_touch).set_raw_correction(${TOUCH_AFFINE_XX}f, ${TOUCH_AFFINE_XY}f, ${TOUCH_AFFINE_XC}f, ${TOUCH_AFFINE_YX}f, ${TOUCH_AFFINE_YY}f, ${TOUCH_AFFINE_YC}f);
    cyd::touch_guard.configure(${TOUCH_MOVE_LIMIT_PX}, ${TOUCH_MIN_PRESS_MS});
    cyd::edge_snap_band = ${SLIDER_EDGE_SNAP_PX};
    id(last_touch_ms) = millis();
    id(last_use_ms) = millis();
    id(display_dimmed) = false;
  # right after runtime_tiles::enabled = true, before the settings load
  BOOT_ENABLED_HOOK: ""
  # the paint style only this board has (theme::paints)
  BOARD_PAINT_FILL: "theme::fill(id(paint_calibration), Paint::calibration);"
  # in runtime_tiles::dismiss, after the detail card closes
  CLOSE_CARDS_HOOK: ""
  # what LVGL's gesture over the tiles does on this board
  BOOT_PAGE_GESTURE: |-
    lv_obj_add_event_cb(id(home_page)->obj, [](lv_event_t *event) {
      if(!runtime_tiles::enabled || !runtime_tiles::swipe_pages || id(display_dimmed) || id(calibration_active) ||
         !id(active_entity).empty() || (runtime_tiles::detail_root && !lv_obj_has_flag(runtime_tiles::detail_root,LV_OBJ_FLAG_HIDDEN)))return;
      auto *input=lv_indev_active();if(!input)return;
      auto direction=lv_indev_get_gesture_dir(input);
      if(direction!=LV_DIR_LEFT && direction!=LV_DIR_RIGHT && direction!=LV_DIR_TOP)return;
      // A quick drag on a slider is that slider's, never a page swipe (firmware 0.2.68+).
      if(runtime_tiles::captured_slider){ESP_LOGI("touch","page swipe ignored: a slider is being dragged");return;}
      cyd::touch_guard.consume();
      lv_indev_wait_release(input);
      // Up is the way home (firmware 0.2.100+); this board has no edge band, so a flick anywhere over the tiles
      // does it, as left and right do here.
      if(direction==LV_DIR_TOP){
        ESP_LOGI("touch","swipe up: back to page 1");
        runtime_tiles::swipe_glow(runtime_tiles::Edge::bottom);
        if(runtime_tiles::back_home)runtime_tiles::back_home();
        return;
      }
      // The edge the page comes from lights up for a moment (firmware 0.2.100+): swiping left brings in the next
      // page from the right.
      runtime_tiles::swipe_glow(direction==LV_DIR_LEFT?runtime_tiles::Edge::right:runtime_tiles::Edge::left);
      ESP_LOGI("touch","page swipe: page %d -> %d",id(tile_page),id(tile_page)+(direction==LV_DIR_LEFT?1:-1));
      id(tile_page)+=direction==LV_DIR_LEFT?1:-1;
      id(show_tile_page).execute();
    }, LV_EVENT_GESTURE, nullptr);
  # after the gesture: the camera images of a board that shows them
  BOOT_CAMERA_HOOKS: ""
  # after the cards' hooks, before the settings page's: the calibration wizard of a resistive panel, the fan and swing card of a big one
  BOOT_CARDS_TAIL: |-
    if (!runtime_tiles::enabled) return;
    screen_calibration::apply = [](const screen_calibration::Calibration &cal) {
      id(ts_touch).set_raw_correction(cal.c[0],cal.c[1],cal.c[2],cal.c[3],cal.c[4],cal.c[5]);
    };
    screen_calibration::isolation = [](bool active) { id(calibration_active) = active; };
    screen_calibration::setup(id(home_page)->obj, id(sublabel)->get_lv_font(), id(headline)->get_lv_font(),
      ${TOUCH_CAL_X_MIN}, ${TOUCH_CAL_X_MAX}, ${TOUCH_CAL_Y_MIN}, ${TOUCH_CAL_Y_MAX});
    // The wizard runs by itself the first time this panel starts; this is the way back to it (firmware 0.2.96+),
    // the same one the Calibrate touch button in Home Assistant takes. Binding it is also what puts Calibrate
    // touch on the settings page: a board that builds no wizard binds nothing and has no row, so the two can
    // never disagree. The page closes first, as its own back arrow closes it: the wizard loads a screen of its
    // own and leaves the home page behind it when it is done.
    settings_screen::calibrate_touch = []() { settings_screen::close(); screen_calibration::begin(); };
  # what else keeps the settings page closed (inline, in the condition)
  MAY_OPEN_EXTRA: ""
  # the log line of a boot onto the touch test page
  TOUCH_TEST_READY_LOG: "USB calibration ready; no tile actions"
  # one more line of the ui_state diagnostics
  UI_STATE_HOOK: ""
  # every 250 ms after runtime_tiles::tick()
  TICK_HOOK: ""
  # apply_screen_settings: how the backlight takes its level
  APPLY_BACKLIGHT: "id(back_light).turn_on().set_transition_length(300).set_brightness(level / 100.0f).perform();"
  # back_to_page_1_when_due: what else counts as away from page 1 (inline)
  AWAY_EXTRA: ""
  # alert_show: after the settings page closed, before the card is made
  ALERT_SHOW_HOOK: ""

# ============================================================
# No need to edit below this line.
# ============================================================

esp32:
  board: esp32dev
  framework:
    type: esp-idf
    advanced:
      # ESP-IDF's assert texts (file, line and condition of every check) stay out of the firmware (0.2.75+), 49 KB on
      # the CYD: a failed assert still stops and restarts the screen, and its backtrace still points at the line.
      # Every board sets it (tests/test_easy_package.py). ESP-IDF's error names stay: without them ESPHome's own
      # error lines only say "UNKNOWN ERROR".
      assertion_level: SILENT
      # ESPHome recommends this itself in the boot log. Yields ~40KB extra IRAM
      # and a smaller binary, and every KB counts on this board.
      sram1_as_iram: true
      # minimum_chip_revision deliberately left out: that was here for the
      # 032c board (rev 3.1 confirmed), but this is a different physical board
      # with an unknown chip revision. With an unjustifiably high minimum
      # the firmware simply refuses to boot.
    version: recommended
  variant: esp32

# Two SPI buses used by the CYD:
# - lcd: built-in display
# - my_touchscreen: built-in touch controller
# Two SPI buses on the 2432S028: one for the display, a separate one for the
# resistive XPT2046 touch (no I2C on this board, unlike the 032c).
spi:
  - id: lcd
    clk_pin: GPIO14
    mosi_pin: GPIO13
    miso_pin: GPIO12

output:
  - platform: ledc
    pin: GPIO27 # Updated for Hosyond board. Was GPIO21 
    id: gpio_backlight_pwm
    # ESPHome's ledc default is 1kHz. You don't see that while still (well
    # above what the eye perceives as flicker), but moving the screen reveals
    # motion blur turning the on/off pulses into streaks (the same effect as
    # a PWM LED that "judders" when you move it). 20kHz sits well above the
    # speed at which motion still makes that visible, and still leaves ample
    # duty-cycle resolution left over for smooth dimming.
    frequency: 20000Hz

  - platform: ledc
    id: output_red
    pin: GPIO4
    inverted: true
  - platform: ledc
    id: output_green
    pin: GPIO16
    inverted: true
  - platform: ledc
    id: output_blue
    pin: GPIO17
    inverted: true

light:
  # The firmware owns the backlight: brightness, standby, night and alerts set it, and the minute tick
  # restores it. Home Assistant uses the brightness numbers and the Wake and Sleep buttons (0.2.46+).
  - platform: monochromatic
    output: gpio_backlight_pwm
    name: "Power Display Backlight"
    id: back_light
    internal: true
    restore_mode: ALWAYS_ON

  - platform: rgb
    name: LED
    red: output_red
    id: led
    green: output_green
    blue: output_blue
    restore_mode: ALWAYS_OFF

# Touchscreen configuration (XPT2046, resistive, via SPI)
# XPT2046 calibration is board-specific. Poll at 50Hz; LVGL rotates touch
# with the display. GPIO36 IRQ is deliberately unused (driver requests a
# pull-up that this input-only pin cannot provide).
touchscreen:
  platform: xpt2046
  id: ts_touch
  spi_id: lcd
  cs_pin: 33
  # Polling avoids GPIO36 pull-up errors and noisy PENIRQ edges.
  update_interval: 20ms
  # Diagnosis caught a false corner contact at pressure 422. Deliberate
  # contacts measured 936-2643; reject the low-pressure tail on release.
  threshold: 800

  calibration:
    x_min: ${TOUCH_CAL_X_MIN}
    x_max: ${TOUCH_CAL_X_MAX}
    y_min: ${TOUCH_CAL_Y_MIN}
    y_max: ${TOUCH_CAL_Y_MAX}

  transform:
    swap_xy: ${TOUCH_SWAP_XY}
    mirror_x: ${TOUCH_MIRROR_X}
    mirror_y: ${TOUCH_MIRROR_Y}

  on_touch:
    - lambda: |-
        id(last_touch_ms) = millis();
        id(last_use_ms) = millis();
        id(touch_down) = true;
        cyd::touch_guard.begin(millis(), touch.x, touch.y, touch.id);
        // The point the screen itself works with, through ESPHome's own turn (the same call the tiles are hit
        // with): the wizard fits against that, so it never has to work out what the turn does.
        int screen_x = touch.x, screen_y = touch.y;
        if (runtime_tiles::touch_input::to_screen) runtime_tiles::touch_input::to_screen(screen_x, screen_y);
        screen_calibration::press(touch.id, screen_x, screen_y, id(ts_touch).filtered_raw_x(), id(ts_touch).filtered_raw_y());
        ESP_LOGI("touch", "press native=%u,%u screen=%d,%d raw=%d,%d pressure=%d calibration=%d", touch.x, touch.y, screen_x, screen_y, id(ts_touch).filtered_raw_x(), id(ts_touch).filtered_raw_y(), touch.z_raw, id(calibration_active));
  on_update:
    - lambda: |-
        id(last_touch_ms) = millis();
        id(last_use_ms) = millis();
        // Only the contact that started the touch counts; the guard ignores other ids.
        for (const auto &point : touches) {
          cyd::touch_guard.update(point.x, point.y, point.id);
          // The wizard keeps both halves of a reading from the same instant (firmware 0.2.97+): where the screen
          // puts this point, and what the panel is sending right now.
          int screen_x = point.x, screen_y = point.y;
          if (runtime_tiles::touch_input::to_screen) runtime_tiles::touch_input::to_screen(screen_x, screen_y);
          screen_calibration::hold(point.id, screen_x, screen_y, id(ts_touch).filtered_raw_x(), id(ts_touch).filtered_raw_y());
        }
  on_release:
    - lambda: |-
        id(last_touch_ms) = millis();
        id(last_use_ms) = millis();
        id(touch_down) = false;
        screen_calibration::release();

# Display configuration (ILI9341)
display:
  - platform: mipi_spi
    id: my_display
    spi_id: lcd
    model: ST7796
    data_rate: 40MHz
    cs_pin: GPIO15
    dc_pin: GPIO2
    invert_colors: false
    update_interval: never
    auto_clear_enabled: false

    dimensions:
      width: 320
      height: 480


lvgl:
  id: screen_lvgl
  style_definitions:
    # The calibration wizard's page and crosses (theme::paints fills it: BOARD_PAINT_FILL).
    - id: paint_calibration

# Turning the screen as a Home Assistant choice (firmware 0.2.80+). This glass is not square, so it offers the half turn; a quarter turn would swap its width
# and height and change how many rows and columns fit. The shared tree applies the
# angle (packages/core.yaml); a list of options cannot come from a substitution, which is why every board carries this
# block and tools/check_packages.py keeps the three alike.
select:
  - platform: template
    id: setting_rotation
    name: "Rotation"
    icon: "mdi:screen-rotation"
    entity_category: config
    options: ["0°", "180°"]
    update_interval: never
    lambda: 'return std::to_string(settings_screen::rotation) + "°";'
    set_action:
      - lambda: |-
          const std::string option(x.c_str(), x.size());
          settings_screen::set("rotation", atoi(option.c_str()));

button:
  - platform: template
    name: "Calibrate touch"
    internal: ${DASHBOARD_INBOX_INTERNAL}
    entity_category: config
    on_press:
      - lambda: 'if (runtime_tiles::enabled) screen_calibration::begin();'

script:
  # Render every overlay and both pages repeatedly without controlling devices.
  - id: ui_self_test
    mode: single
    then:
      - logger.log:
          format: "UI_TEST START: page/overlay render stress, no HA actions"
          level: INFO  # the core logs from INFO; logger.log is DEBUG by default
      - lambda: 'ESP_LOGI("ui_test", "Light sliders: %s", light_controls::self_test() ? "PASS" : "FAIL");'
      - lambda: 'id(calibration_active) = false;'
      - lvgl.page.show: home_page
      - script.execute: wake_display
      - repeat:
          count: 10
          then:
            - lambda: |-
                id(last_touch_ms) = millis();
                id(last_use_ms) = millis();
                static unsigned test_page=0;
                id(tile_page) = runtime_tiles::enabled ? test_page++ % runtime_tiles::page_count() : 1;
            - script.execute: show_tile_page
            - delay: 300ms
            - lambda: |-
                lv_obj_update_layout(id(tile_scroll));
                const int count = runtime_tiles::model.count;
                // Wide cards take two slots, so visibility follows the runtime packing.
                const bool paginated = runtime_tiles::enabled ? runtime_tiles::page_count() > 1 : count > 6;
                // One card per cell of this board's grid: it is on screen when the page has a tile for its cell.
                bool ok = true;
                for (auto &w : runtime_tiles::widgets) {
                  bool visible = w.index < runtime_tiles::model.count;
                  ok = ok && w.tile && (lv_obj_has_flag(w.tile, LV_OBJ_FLAG_HIDDEN) != visible);
                }
                // The bar shows with more than one page, unless the Page buttons setting is off (firmware 0.2.69+).
                const bool bar = paginated && (!runtime_tiles::enabled || settings_screen::page_buttons);
                for (lv_obj_t* control : {id(page_prev), id(page_next), id(page_number)})
                  ok = ok && (lv_obj_has_flag(control, LV_OBJ_FLAG_HIDDEN) != bar);
                lv_area_t nav, grid;
                lv_obj_get_coords(id(page_next), &nav);
                lv_obj_get_coords(id(tile_scroll), &grid);
                ok = ok && (!bar || grid.y2 < nav.y1);
                if(runtime_tiles::enabled)ok = runtime_tiles::check_tile_geometry() && ok;
                ESP_LOGI("ui_test", "page_check=%s count=%d heap=%u frames=%u grid_bottom=%d nav_top=%d", ok ? "PASS" : "FAIL", count, (unsigned) esp_get_free_heap_size(), (unsigned) id(rendered_frames), (int) grid.y2, (int) nav.y1);
                id(tile_page) = 0;
            - script.execute: show_tile_page
            - delay: 150ms
            - lvgl.widget.show: color_detail_overlay
            - delay: 150ms
            - lvgl.widget.hide: color_detail_overlay
            - lvgl.widget.show: alert_overlay
            - delay: 150ms
            - lvgl.widget.hide: alert_overlay
      - lambda: 'if (!id(ui_refresh).is_running()) id(ui_refresh).execute();'
      - logger.log:
          format: "UI_TEST COMPLETE"
          level: INFO

  # The two scripts that drive the backlight as a light action; the Guition drives it through its set_backlight script.
  - id: alert_dismiss
    parameters:
      reason: string
    then:
      - if:
          condition:
            lambda: 'return id(alert_active);'
          then:
            - lambda: |-
                id(alert_active) = false;
                id(last_touch_ms) = millis();
                ESP_LOGI("alert", "dismissed: %s", reason.c_str());
            - lvgl.widget.hide: alert_overlay
            - script.stop: alert_timeout
            - script.stop: alert_flash
            - light.turn_on:
                id: back_light
                transition_length: 80ms
                brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
            - script.execute:
                id: alert_report
                action: !lambda 'return reason;'

  # Four backlight blinks when the alert arrives; the screen then stays on as usual.
  - id: alert_flash
    mode: restart
    then:
      - repeat:
          count: 4
          then:
            - light.turn_off:
                id: back_light
                transition_length: 0s
            - delay: 120ms
            - light.turn_on:
                id: back_light
                transition_length: 0s
                brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
            - delay: 170ms

  - id: wake_display
    mode: restart
    then:
      - lambda: |-
          id(last_touch_ms) = millis();
          id(display_dimmed) = false;
          id(sleep_requested) = false;
      - light.turn_on:
          id: back_light
          # Almost instant brightness-up. ESPHome's default is 1s, and that feels sluggish
          # when you touch the screen and are waiting on light.
          transition_length: 80ms
          brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
      - lvgl.widget.hide: dim_wake_overlay
      - script.execute: close_cards
packages/hosyond.yaml| Click to expand
# ESP Screens - the package a Hosyond CYD (ESP32-32E) builds from over GitHub. ESP Screen Manager writes every
# screen's own YAML with `packages: display: {url: <this repository>, files: [packages/cyd.yaml]}` and adds the
# name, the Wi-Fi reference and the unique keys (docs/EASY_SETUP.md); nothing of that lives here.
#
# The screen is two packages: core.yaml, which every board shares (the LVGL tree, the cards, the scripts, the
# entities), and boards/hosyond-esp32-4in.yaml with this board's hardware and sizes. docs/PROFILES.md says how the
# files fit together.
substitutions:
  FONT_DIR: "https://raw.githubusercontent.com/MaxGramser/homeassistant_espscreen/main/fonts"

packages:
  core: !include core.yaml
  board: !include boards/hosyond-esp32-4in.yaml

external_components:
  - source:
      type: git
      url: https://github.com/MaxGramser/homeassistant_espscreen.git
      ref: main
      path: components
    refresh: 0s
    components: [xpt2046, smart_display]

-Eric

Originally shared on GitHubOriginal authors and dates are preserved.

View original

Discussion 12

Add a comment
Crazyraf87

Come posso utilizzare questi file? non riesco a capire dove inserire o copiare questi file. Grazie

Original on GitHub

afyounie

Come posso utilizzare questi file? non riesco a capire dove inserire o copiare questi file. Grazie

If you are not familiar with where these files go or how to replace them, I would recommend waiting until Max incorporates these changes into the official build. Best to avoid messing up your Esp screen manager install.

-Eric

Original on GitHub

MaxGramser

@afyounie thank you for the configuration, it made this quick. The Hosyond 4-inch ESP32-32E display is a board of its own in 0.3.13, which goes live in the coming hours:

  • New screen in ESP Screens offers the Hosyond 4 inch: the ST7796 display with resistive XPT2046 touch, 480 × 320 lying down with two by three tiles, or one column of four standing up.
  • It calibrates its touch on the first start, the same way the 2.8-inch CYD does, so no manual numbers are needed.
  • It is marked experimental: it is built from your configuration and the manufacturer's pin table, but nobody on the project has run it on a real unit yet. docs/HOSYOND40.md has the details.

@Crazyraf87 you don't need to copy any files: once 0.3.13 is out, update the add-on, open New screen, pick Hosyond 4 inch and flash it over USB from there.

I'll leave this issue open until someone has it running. If you try it, I'd love to hear what works and what doesn't, especially the touch after calibration and the colours.


Got a screen running? Tell others which board you have and what works on the Tessera website. It helps everyone pick a screen that works.

Like my work? Consider buying me a coffee, much appreciated!

Original on GitHub

erbstueck

· Edited

Hi, I'm facing the following issue at the backlight brightness settings: Step between 100% and 95% way too big 100% -> full brightness 95% -> way too dark, not easy to read even in dark environment. 0% to 95% -> The gradations look just right.

Screen FW: 0.13.0 Tessera: 0.4.22

Update: Same behavior on the CYD 2.8" display https://www.amazon.de/dp/B0FF4DTNCG

Original on GitHub

Crazyraf87

I have a screen calibration issue affecting only one corner. Could it be a firmware problem?

Original on GitHub

MaxGramser

Can you elaborate a bit more about your problem?

Original on GitHub

erbstueck

I have a screen calibration issue affecting only one corner. Could it be a firmware problem?

I didn't notice that during my setup

Original on GitHub

afyounie

I have a screen calibration issue affecting only one corner. Could it be a firmware problem?

I had this issue as well. I attribute it to the hardware as adjacent corners worked fine. I eventually got it to work by using my finger to tap the problem corner. Calibration completed and recognizes taps in the correct location.

Original on GitHub

afyounie

Hi, I'm facing the following issue at the backlight brightness settings: Step between 100% and 95% way too big 100% -> full brightness 95% -> way too dark, not easy to read even in dark environment. 0% to 95% -> The gradations look just right.

Screen FW: 0.13.0 Tessera: 0.4.22

Update: Same behavior on the CYD 2.8" display https://www.amazon.de/dp/B0FF4DTNCG

I am experiencing this as well on both the Hosyond and the CYD 2.8. You should probably start a new issue ticket as this isn't specific to the Hosyond board.

Original on GitHub

MaxGramser

@erbstueck @afyounie thanks for reporting this, and sorry it took a while. You were both right, and it was not the screen settings at all.

The backlight was driven at 20 kHz. The part on these boards that switches the backlight cannot keep up at that speed, so only 100 % (always on) looked right, and anything below came out far too dark. It now runs at 1 kHz on the CYD and on this Hosyond 4-inch board. I tested it on a CYD: 95 % now looks almost the same as 100 %, 50 % is clearly half, and even 5 % is still faintly visible, all without any flicker.

It is in ESP Screen Manager 0.4.48 (firmware 0.29.0):

  1. Update ESP Screen Manager in Home Assistant.
  2. Press Update on each CYD and Hosyond screen.

I don't have a Hosyond myself, so I'd love to hear whether it now dims evenly on yours too.


Got a screen running? Tell others which board you have and what works on the Tessera website. It helps everyone pick a screen that works.

Like my work? Consider buying me a coffee, much appreciated!

Original on GitHub

erbstueck

Hi @MaxGramser, successfully tested on both Hosyond 4" and 2.8“ CYD. Works great, thanks for your effort.

Original on GitHub

afyounie

I finally got a chance to test the latest update on the Hosyond 4 in. Screen brightness works perfectly. Screen configures perfectly with no issues. Thanks for adding the screen to the official release.

-Eric

Original on GitHub

Add your thoughts, a screenshot or a useful link.

Markdown & basic HTML
Paste or drop images · URLs on their own line become previews0 / 10,000
Sign in with GitHub to commentYour draft stays in this browser tab.