Implementation is really tied to your HA entities, but In case it could serve your inspiration, here’s what I end up with
Note the native display page feature of ESP Home is not working on this display type. had to rebuild it manually
esphome:
name: smalltv-esp32
friendly_name: smalltv_esp32
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret api
ota:
password: !secret ota_paswd
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Smalltv-Esp32 Fallback Hotspot"
password: "NOtnhLeYNfPB"
captive_portal:
external_components:
- source:
type: git
url: https://github.com/rletendu/esphome.git
ref: st7789_no_frame_buffer
refresh: 0s
components: [st7789v]
#captive_portal:
debug:
update_interval: 5s
sensor:
- platform: homeassistant
entity_id: sensor.abri_bois_am2301_temperature
id: abri_bois
- platform: homeassistant
entity_id: sensor.atc_mez41e_641e_temperature
id: mezzanine
- platform: homeassistant
entity_id: sensor.piscine_temperature
id: piscine
- platform: homeassistant
entity_id: sensor.atc_sal505_6505_temperature
id: salon
- platform: homeassistant
entity_id: sensor.atc_julb28_3b28_temperature
id: juliette
- platform: homeassistant
entity_id: sensor.atc_vic77a_d77a_temperature
id: victorien
- platform: homeassistant
entity_id: sensor.atc_cle63e_a63e_temperature
id: clemence
- platform: homeassistant
entity_id: sensor.atc_sdb80a_880a_temperature
id: sdb_etage
- platform: homeassistant
entity_id: sensor.atc_cui76c_576c_temperature
id: cuisine
- platform: homeassistant
entity_id: sensor.atc_cab2c2_temperature
id: cabane_ext
- platform: homeassistant
entity_id: sensor.garage_temp
id: garage
- platform: homeassistant
entity_id: sensor.nodered_f80113f7800c75ed
id: girouette
- platform: homeassistant
entity_id: weather.varades
attribute: pressure
id: pressure_weather
- platform: homeassistant
entity_id: sensor.girouette_pression
id: pressure
- platform: homeassistant
name: "T_max_day0"
entity_id: sensor.T_max_day0
id: T_max_day0
- platform: homeassistant
name: "T_max_day1"
entity_id: sensor.T_max_day1
id: T_max_day1
- platform: homeassistant
name: "T_max_day2"
entity_id: sensor.T_max_day2
id: T_max_day2
- platform: homeassistant
name: "T_max_day3"
entity_id: sensor.T_max_day3
id: T_max_day3
- platform: homeassistant
name: "T_max_day4"
entity_id: sensor.T_max_day4
id: T_max_day4
- platform: homeassistant
name: "T_min_day0"
entity_id: sensor.T_min_day0
id: T_min_day0
- platform: homeassistant
name: "T_min_day1"
entity_id: sensor.T_min_day1
id: T_min_day1
- platform: homeassistant
name: "T_min_day2"
entity_id: sensor.T_min_day2
id: T_min_day2
- platform: homeassistant
name: "T_min_day3"
entity_id: sensor.T_min_day3
id: T_min_day3
- platform: homeassistant
name: "T_min_day4"
entity_id: sensor.T_min_day4
id: T_min_day4
- platform: homeassistant
name: "Precipitation_day0"
entity_id: sensor.Precipitation_day0
id: Precipitation_day0
- platform: homeassistant
name: "Precipitation_day1"
entity_id: sensor.Precipitation_day1
id: Precipitation_day1
- platform: homeassistant
name: "Precipitation_day2"
entity_id: sensor.Precipitation_day2
id: Precipitation_day2
- platform: homeassistant
name: "Precipitation_day3"
entity_id: sensor.Precipitation_day3
id: Precipitation_day3
- platform: homeassistant
name: "Precipitation_day4"
entity_id: sensor.Precipitation_day4
id: Precipitation_day4
- platform: homeassistant
name: "index_day0"
entity_id: sensor.index_day0
id: index_day0
text_sensor:
- platform: homeassistant
name: "Condition_day0"
entity_id: sensor.Condition_day0
id: Condition_day0
- platform: homeassistant
name: "Condition_day1"
entity_id: sensor.Condition_day1
id: Condition_day1
- platform: homeassistant
name: "Condition_day2"
entity_id: sensor.Condition_day2
id: Condition_day2
- platform: homeassistant
name: "Condition_day3"
entity_id: sensor.Condition_day3
id: Condition_day3
- platform: homeassistant
name: "Condition_day4"
entity_id: sensor.Condition_day4
id: Condition_day4
# Define a PWM output on the ESP32
output:
- platform: ledc
pin: GPIO25
inverted: True
id: backlight_pwm
- platform: ledc
pin: GPIO12
id: rtttl_out
rtttl:
output: rtttl_out
# Define a monochromatic, dimmable light for the backlight
light:
- platform: monochromatic
output: backlight_pwm
name: "Display Backlight"
id: back_light
restore_mode: ALWAYS_ON
esp32_touch:
# setup_mode: true
globals:
- id: page
type: int
initial_value: "1"
interval:
- interval: 20s
then:
- lambda: |-
/*
id(page) = (id(page) + 1);
if (id(page) > 3) {
id(page) = 1;
} */
binary_sensor:
- platform: homeassistant
entity_id: binary_sensor.status_porte_garage
id: porte_garage
- platform: homeassistant
entity_id: binary_sensor.portail_lock_sense
id: portail
- platform: esp32_touch
name: "ESP32 Touch Pad GPIO32"
pin: GPIO32
threshold: 1250
id: gp32
on_click:
- min_length: 50ms
max_length: 350ms
then:
- if:
condition:
- light.is_off: back_light
then:
- light.turn_on:
id: back_light
else:
- lambda: |-
id(page) = (id(page) + 1);
if (id(page) > 3) {
id(page) = 1;
}
- component.update : disp
- min_length: 1000ms
max_length: 2000ms
then:
- light.turn_off: back_light
- rtttl.play: 'two_short:d=4,o=5,b=100:16e6,16e6'
- platform: esp32_touch
name: "ESP32 Touch Pad GPIO33"
pin: GPIO33
threshold: 1375
id: gp33
on_click:
- min_length: 50ms
max_length: 350ms
then:
- if:
condition:
- light.is_off: back_light
then:
- light.turn_on:
id: back_light
else:
- lambda: |-
id(page) = (id(page) - 1);
if (id(page) < 1) {
id(page) = 3;
}
- component.update : disp
- min_length: 1000ms
max_length: 2000ms
then:
- light.turn_off: back_light
number:
- platform: template
name: "disp page"
optimistic: true
min_value: 0
max_value: 4
step: 1
on_value:
then:
- lambda: |-
id(page) = x;
- component.update : disp
font:
- file: 'OpenSans-Regular.ttf'
id: font20
size: 20
- file: 'OpenSans-Regular.ttf'
id: font60
size: 50
- file: 'fonts/materialdesignicons-webfont.ttf'
id: fonticon
size: 50
glyphs: &mdi-weather-glyphs
- "\U000F0590" # mdi-weather-cloudy
- "\U000F0F2F" # mdi-weather-cloudy-alert
- "\U000F0E6E" # mdi-weather-cloudy-arrow-right
- "\U000F0591" # mdi-weather-fog
- "\U000F0592" # mdi-weather-hail
- "\U000F0F30" # mdi-weather-hazy
- "\U000F0898" # mdi-weather-hurricane
- "\U000F0593" # mdi-weather-lightning
- "\U000F067E" # mdi-weather-lightning-rainy
- "\U000F0594" # mdi-weather-night
- "\U000F0F31" # mdi-weather-night-partly-cloudy
- "\U000F0595" # mdi-weather-partly-cloudy
- "\U000F0F32" # mdi-weather-partly-lightning
- "\U000F0F33" # mdi-weather-partly-rainy
- "\U000F0F34" # mdi-weather-partly-snowy
- "\U000F0F35" # mdi-weather-partly-snowy-rainy
- "\U000F0596" # mdi-weather-pouring
- "\U000F0597" # mdi-weather-rainy
- "\U000F0598" # mdi-weather-snowy
- "\U000F0F36" # mdi-weather-snowy-heavy
- "\U000F067F" # mdi-weather-snowy-rainy
- "\U000F0599" # mdi-weather-sunny
- "\U000F0F37" # mdi-weather-sunny-alert
- "\U000F14E4" # mdi-weather-sunny-off
- "\U000F059A" # mdi-weather-sunset
- "\U000F059B" # mdi-weather-sunset-down
- "\U000F059C" # mdi-weather-sunset-up
- "\U000F0F38" # mdi-weather-tornado
- "\U000F059D" # mdi-weather-windy
- "\U000F059E" # mdi-weather-windy-variant
- "\U000F058C" # mdi-water
- "\U000F17FB" # mdi-GarageLock
- "\U000F06DA" # mdi-GarageOpen
- "\U000F116A" # mid-GateOpen
- "\U000F0299" # mdi-GateClose
- "\U000F06EE" # mdi-mailbox
- "\U000F0A79" # mdi-trash-can
- "\U000F001B" # mdi-Air-conditioner
- "\U000F0D91" # mdi-MotionSensor-ON
- "\U000F1435" # mdi-MotionSensor-Off
- file: 'fonts/materialdesignicons-webfont.ttf'
id: fonticon20
size: 20
glyphs: &mdi-weather-glyphs20
- "\U000F058C" # mdi-water
- "\U000F005C" # mdi-ArrowTopRight
- "\U000F0043" # mdi-ArrowBottomRight"
- "\U000F092B" # no-wifi
- "\U000F092F" # low-wifi
- "\U000F091F" # wifi-1
- "\U000F0922" # wifi-2
- "\U000F0925" # wifi-3
- "\U000F0928" # wifi-4
color:
- id: color_green
red: 0%
green: 100%
blue: 0%
- id: color_red
red: 100%
green: 0%
blue: 0%
- id: color_blue
red: 0%
green: 0%
blue: 100%
- id: color_orange
red: 100%
green: 64%
blue: 0%
spi:
clk_pin: GPIO18
mosi_pin: GPIO23
interface: hardware
id: spihwd
graph:
- id: pressure_graph
sensor: pressure
duration: 24h
width: 180
height: 100
color: color_orange
- id: multi_temperature_graph
duration: 24h
width: 180
height: 100
traces:
- sensor: abri_bois
line_type: DOTTED
color: color_blue
- sensor: salon
line_type: SOLID
color: color_green
display:
- platform: st7789v
model: "Custom"
spi_id: spihwd
height: 240
width: 240
offset_height: 0
offset_width: 0
dc_pin: GPIO02
reset_pin: GPIO04
#backlight_pin: GPIO25
eightbitcolor: True
#update_interval: never
update_interval: 5s
id: disp
lambda: |-
int y;
int x;
//--------------------------------------------------------------------------
// Map weather states to MDI characters.
//--------------------------------------------------------------------------
std::map<std::string, std::string> weather_icon_map
{
{"cloudy", "\U000F0590"},
{"cloudy-alert", "\U000F0F2F"},
{"cloudy-arrow-right", "\U000F0E6E"},
{"fog", "\U000F0591"},
{"hail", "\U000F0592"},
{"hazy", "\U000F0F30"},
{"hurricane", "\U000F0898"},
{"lightning", "\U000F0593"},
{"lightning-rainy", "\U000F067E"},
{"night", "\U000F0594"},
{"clear-night", "\U000F0594"},
{"night-partly-cloudy", "\U000F0F31"},
{"partlycloudy", "\U000F0595"},
{"partly-lightning", "\U000F0F32"},
{"partly-rainy", "\U000F0F33"},
{"partly-snowy", "\U000F0F34"},
{"partly-snowy-rainy", "\U000F0F35"},
{"pouring", "\U000F0596"},
{"rainy", "\U000F0597"},
{"snowy", "\U000F0598"},
{"snowy-heavy", "\U000F0F36"},
{"snowy-rainy", "\U000F067F"},
{"sunny", "\U000F0599"},
{"sunny-alert", "\U000F0F37"},
{"sunny-off", "\U000F14E4"},
{"sunset", "\U000F059A"},
{"sunset-down", "\U000F059B"},
{"sunset-up", "\U000F059C"},
{"tornado", "\U000F0F38"},
{"windy", "\U000F059D"},
{"windy-variant", "\U000F059E"},
};
std::string weekdays[7]={"Lun","Mar","Mer","Jeu","Ven","Sam","Dim"};
switch (id(page)){
default:
case 0:
case 1:
it.printf(0, 0, id(font60), "%.1f°",id(abri_bois).state);
if (id(Precipitation_day0).state) {
it.printf(130, 5, id(fonticon20), id(color_blue),"\U000F058C");
it.printf(145, 0, id(font20), id(color_blue),"%.0f",id(Precipitation_day0).state);
}
it.printf(130, 20, id(font20), id(color_red), "%.0f°",id(T_max_day0).state);
it.printf(130, 40, id(font20), id(color_green), "%.0f°",id(T_min_day0).state);
it.printf(190, 20, id(fonticon), "%s", weather_icon_map[id(Condition_day0).state.c_str()].c_str());
it.printf(0, 60, id(font20), id(color_orange), "%.0fhPa",id(pressure).state);
// Days name for forecast
y = 100;
x = 5;
it.printf(x, y, id(font20), "%s", weekdays[(int)(id(index_day0).state+1)%7].c_str());
x += 60;
it.printf(x, y, id(font20), "%s", weekdays[(int)(id(index_day0).state+2)%7].c_str());
x += 60;
it.printf(x, y, id(font20), "%s", weekdays[(int)(id(index_day0).state+3)%7].c_str());
x += 60;
it.printf(x, y, id(font20), "%s", weekdays[(int)(id(index_day0).state+4)%7].c_str());
//Forecast
// Day +1
y = 122;
x = 5;
it.printf(x, y, id(fonticon), "%s", weather_icon_map[id(Condition_day1).state.c_str()].c_str());
it.printf(x, y+40, id(font20), id(color_red), "%.0f°",id(T_max_day1).state);
it.printf(x, y+60, id(font20), id(color_green), "%.0f°",id(T_min_day1).state);
if (id(Precipitation_day1).state) {
it.printf(x, y+85, id(fonticon20), id(color_blue),"\U000F058C");
it.printf(x+15, y+80, id(font20), id(color_blue),"%.0f",id(Precipitation_day1).state);
}
// Day + 2
x += 60;
it.printf(x, y, id(fonticon), "%s", weather_icon_map[id(Condition_day2).state.c_str()].c_str());
it.printf(x, y+40, id(font20), id(color_red), "%.0f°",id(T_max_day2).state);
it.printf(x, y+60, id(font20), id(color_green), "%.0f°",id(T_min_day2).state);
if (id(Precipitation_day2).state) {
it.printf(x, y+85, id(fonticon20), id(color_blue),"\U000F058C");
it.printf(x+15, y+80, id(font20), id(color_blue),"%.0f",id(Precipitation_day2).state);
}
// Day + 3
x += 60;
it.printf(x, y, id(fonticon), "%s", weather_icon_map[id(Condition_day3).state.c_str()].c_str());
it.printf(x, y+40, id(font20), id(color_red), "%.0f°",id(T_max_day3).state);
it.printf(x, y+60, id(font20), id(color_green), "%.0f°",id(T_min_day3).state);
if (id(Precipitation_day3).state) {
it.printf(x, y+85, id(fonticon20), id(color_blue),"\U000F058C");
it.printf(x+15, y+80, id(font20), id(color_blue),"%.0f",id(Precipitation_day3).state);
}
// Day + 4
x += 60;
it.printf(x, y, id(fonticon), "%s", weather_icon_map[id(Condition_day4).state.c_str()].c_str());
it.printf(x, y+40, id(font20), id(color_red), "%.0f°",id(T_max_day4).state);
it.printf(x, y+60, id(font20), id(color_green), "%.0f°",id(T_min_day4).state);
if (id(Precipitation_day4).state) {
it.printf(x, y+85, id(fonticon20), id(color_blue),"\U000F058C");
it.printf(x+15, y+80, id(font20), id(color_blue),"%.0f",id(Precipitation_day4).state);
}
break;
case 2:
x = 5;
y = 0;
it.printf(x, y, id(font20), "Abri");
it.printf(x, y+20, id(font20), "%.1f°",id(abri_bois).state);
x += 60;
it.printf(x, y, id(font20), "CabE.");
it.printf(x, y+20, id(font20), "%.1f°",id(cabane_ext).state);
x += 60;
it.printf(x, y, id(font20), "Gir.");
it.printf(x, y+20, id(font20), "%.1f°",id(girouette).state);
x += 60;
it.printf(x, y, id(font20), "Pisc.");
it.printf(x, y+20, id(font20), "%.1f°",id(piscine).state);
x = 5;
y = 50;
it.printf(x, y, id(font20), "Juju");
it.printf(x, y+20, id(font20), "%.1f°",id(juliette).state);
x += 60;
it.printf(x, y, id(font20), "Clem");
it.printf(x, y+20, id(font20), "%.1f°",id(clemence).state);
x += 60;
it.printf(x, y, id(font20), "Vict");
it.printf(x, y+20, id(font20), "%.1f°",id(victorien).state);
x += 60;
it.printf(x, y, id(font20), "Sdb Et.");
it.printf(x, y+20, id(font20), "%.1f°",id(sdb_etage).state);
x = 5;
y = 100;
it.printf(x, y, id(font20), "Mezz");
it.printf(x, y+20, id(font20), "%.1f°",id(mezzanine).state);
x += 60;
it.printf(x, y, id(font20), "Salon");
it.printf(x, y+20, id(font20), "%.1f°",id(salon).state);
x += 60;
it.printf(x, y, id(font20), "Cuis.");
it.printf(x, y+20, id(font20), "%.1f°",id(cuisine).state);
x += 60;
it.printf(x, y, id(font20), "Garge");
it.printf(x, y+20, id(font20), "%.1f°",id(garage).state);
x = 5;
y = 150;
it.printf(x, y, id(font20), "Garge");
if (id(porte_garage).state) {
it.printf(x, y+20, id(fonticon),id(color_green), "\U000F06DA"); // Open
} else {
it.printf(x, y+20, id(fonticon),id(color_red), "\U000F17FB"); // Lock
}
x += 60;
it.printf(x, y, id(font20), "Portail");
if (id(portail).state) {
it.printf(x, y+20, id(fonticon),id(color_green), "\U000F116A"); // Gate Open
} else {
it.printf(x, y+20, id(fonticon),id(color_red), "\U000F0299"); // Gate Lock
}
break;
case 3:
it.graph(0, 0, id(pressure_graph));
it.printf(190, 0, id(font20), id(color_orange), "%.0f",id(pressure).state);
it.printf(190, 20, id(font20), id(color_orange), "hPa");
it.graph(0, 120, id(multi_temperature_graph));
it.printf(190, 120, id(font20), id(color_blue), "Ext");
it.printf(190, 140, id(font20), id(color_blue), "%.1f°",id(abri_bois).state);
it.printf(190, 160, id(font20), id(color_green), "Int");
it.printf(190, 180, id(font20), id(color_green), "%.1f°",id(salon).state);
break;
}