2025/10/24

SmallTV Pro


smalltv ultra & smalltv pro

smalltv pro는 esp32 모델이라 기존 ble gateway로 사용하던 장치를 이기기로 통합 대체하려고 구매.

오리지널 펌에서 최초 포럼에서 받은 구성파일로 만든 펌의 OTA 업로딩은 문제없었는데 ble_tracker세팅하고 ble장치를 추가한 펌에서 먹통이 돼 분해해서 핀헤더 납땜후 시리얼로 다시 펌업.

어떤 문제때문인지 몰라도 framework을 esp-idf로 바꾼후 OTA문제 해결. 처음 LVGL로 시계만들고 구성하다가 기존 esp8266에서 쓰던 코드를 그대로 복붙하고 마무리.

** 버스도착정보 출력을 에너지사용량으로 교체.
** 터치센서는 펌 교체하면서 용도가 애매해짐.

substitutions:
  name: smalltv-pro

esphome:
  name: ${name}
  area: Livingroom
  on_boot:
    priority: 250
    then:
      - delay: 15s
      - lambda: |-
          id(ble_tracker).set_scan_continuous(true); 
          id(ble_tracker).start_scan();

esp32:
  board: esp32dev
  framework:
    type: esp-idf

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "${name} hotspot"
    password: !secret wifi_password

captive_portal:
logger:
api:
ota:
  - platform: esphome
    password: !secret ota_password

external_components:
  - source:
      type: git
      url: https://github.com/lhartmann/esphome-st7789v-fractional-framebuffer
      ref: main
    components: [st7789v]

esp32_ble_tracker:
  id: ble_tracker
  scan_parameters:
    interval: 300ms
    window: 300ms
    active: false

esp32_touch:

binary_sensor:
  - platform: template
    name: "daytime"
    id: daytime
    internal: true
    condition:
      sun.is_above_horizon:
  - platform: esp32_touch
    name: "Touch Button"
    pin: GPIO32
    threshold: 1250

sensor:
  - platform: xiaomi_miscale
    mac_address:
    weight:
      name: "MiScale Weight"
    impedance:
      name: "MiScale Impedance"

  - platform: xiaomi_hhccjcy01
    mac_address:
    temperature:
      name: "Flowerpot Temperature"
    moisture:
      name: "Flowerpot Moisture"
    illuminance:
      name: "Flowerpot Illuminance"
    conductivity:
      name: "Flowerpot Soil Conductivity"

  - platform: xiaomi_hhccjcy01
    mac_address:
    temperature:
      name: "Flowerpot2 Temperature"
    moisture:
      name: "Flowerpot2 Moisture"
    illuminance:
      name: "Flowerpot2 Illuminance"
    conductivity:
      name: "Flowerpot2 Soil Conductivity"

  - platform: xiaomi_lywsdcgq
    mac_address:
    temperature:
      name: "Balcony Temperature"
    humidity:
      name: "Balcony Humidity"
    battery_level:
      name: "Balcony Battery Level"

  - platform: pvvx_mithermometer
    mac_address:
    temperature:
      name: Livingroom Temperature
    humidity:
      name: Livingroom Humidity
    battery_level:
      name: Livingroom Battery
    battery_voltage:
      name: Livingroom Voltage
    signal_strength:
      name: Livingroom Signal

  - platform: pvvx_mithermometer
    mac_address:
    temperature:
      name: "Bathroom Temperature"
    humidity:
      name: "Bathroom Humidity"
    battery_level:
      name: "Bathroom Battery-Level"
    battery_voltage:
      name: "Bathroom Battery-Voltage"
    signal_strength:
      name: "Bathroom Signal"

  - platform: pvvx_mithermometer
    mac_address:
    temperature:
      name: "Kitchen Temperature"
    humidity:
      name: "Kitchen Humidity"
    battery_level:
      name: "Kitchen Battery-Level"
    battery_voltage:
      name: "Kitchen Battery-Voltage"
    signal_strength:
      name: "Kitchen Signal"

output:
  - platform: ledc
    pin: GPIO25
    inverted: true
    id: backlight_pwm

light:
  - platform: monochromatic
    output: backlight_pwm
    name: "Backlight"
    id: back_light
    restore_mode: ALWAYS_ON
    on_turn_on:
      - light.turn_on:
          id: back_light
          brightness: 0.5

spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  interface: hardware
  id: spihwd

time:
  - platform: sntp
    id: ha_time
    timezone: Asia/Seoul
    servers:
      - time.bora.net
      - time.windows.com
      - time.google.com
    on_time:
          - seconds: 0
            then:
              - component.update: disp

sun:
  latitude: 35.8
  longitude: 128.6

switch:
  - platform: restart
    name: "Restart"

text_sensor:
  - platform: homeassistant
    id: power1
    entity_id: sensor.shellyem_power_int
    internal: true
    on_value:
      then:
        - component.update: disp
  - platform: homeassistant
    id: power2
    entity_id: sensor.be_tv_outlet_power
    internal: true
    on_value:
      then:
        - component.update: disp
  - platform: homeassistant
    id: power3
    entity_id: sensor.be_pc_outlet_power
    internal: true
    on_value:
      then:
        - component.update: disp

font:
  - file: "gfonts://Monoton"
    id: monoton
    size: 65
    glyphs: "! %./0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZan°"
  - file: "gfonts://Monomaniac+One"
    id: mono
    size: 35
    glyphs: "! %./0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZan°"
  - file: "gfonts://Doto"
    id: mono2
    size: 40
    glyphs: "! %./0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZan°"
  - file: "gfonts://Vina+Sans"
    id: vina
    size: 35
    glyphs: "! %./0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZan°"

color:
  - id: black
    hex: "000000"
  - id: white
    hex: "FFFFFF"
  - id: cyan
    hex: "00FFFF"
  - id: lightcyan
    hex: "E0FFFF"
  - id: aquamarine
    hex: "7FFFD4"
  - id: teal
    hex: "008080"
  - id: brown
    hex: "D2691E"
  - id: pink
    hex: "FF69B4"
  - id: violet
    hex: "7F00FF"
  - id: darkviolet
    hex: "9400D3"
  - id: salmon
    hex: "FA8072"
  - id: gold
    hex: "FFD700"
  - id: magenta
    hex: "FF00FF"
  - id: red
    hex: "FF0000"
  - id: neonred
    hex: "FC1723"
  - id: orangered
    hex: "FF4500"
  - id: coral
    hex: "FF7F50"
  - id: orange
    hex: "FFA500"
  - id: yellow
    hex: "FFFF00"
  - id: green
    hex: "008000"
  - id: lime
    hex: "00FF00"
  - id: deepskyblue
    hex: "00BFFF"
  - id: royalblue
    hex: "4169E1"
  - id: blue
    hex: "0000FF"
  - id: navy
    hex: "000080"
  - id: purple
    hex: "800080"

display:
  - platform: st7789v
    model: "Custom"
    spi_id: spihwd
    height: 240
    width: 240
    offset_height: 0
    offset_width: 0
    fragmentation: 3
    dc_pin: GPIO02
    reset_pin: GPIO04
    eightbitcolor: True
    update_interval: never
    id: disp
    spi_mode: mode3
    lambda: |-
      int center_x = it.get_width() / 2;
      int center_y = it.get_height() / 2;

      auto time = id(ha_time).now();
      time_t time_t_value = time.timestamp;

      char buf_time[16];
      strftime(buf_time, sizeof(buf_time), "%H:%M", localtime(&time_t_value));

      auto time_color = id(white);
      if (id(daytime).state) {
        time_color = id(neonred);
      } else {
        time_color = id(darkviolet);
      }

      it.printf(center_x, 0, id(monoton), time_color, TextAlign::TOP_CENTER, buf_time);

      it.printf(30, center_y, id(vina), id(gold), TextAlign::TOP_LEFT, "PO :");
      it.printf(100, center_y, id(vina), id(teal), TextAlign::TOP_LEFT, id(power1).state.c_str());

      it.printf(30, center_y + 30, id(vina), id(gold), TextAlign::TOP_LEFT, "TV :");
      it.printf(100, center_y + 30, id(vina), id(teal), TextAlign::TOP_LEFT, id(power2).state.c_str());

      it.printf(30, center_y + 60, id(vina), id(gold), TextAlign::TOP_LEFT, "PC :");
      it.printf(100, center_y + 60, id(vina), id(teal), TextAlign::TOP_LEFT, id(power3).state.c_str());