Hello everyone,
i want just share my current project in its first iteration.
I wanted to build a smart mirror which displays a few important information. It should be at the exterior door and replace our current (stupid) mirror so also my GF supports my ideas/expenses.
Material:
- TTGO T4 V1.3 ILI9341 2,4 inch LCD Display with esp32 LINK
- one sided mirror foil LINK
- old picture frame
My Star Trek affection is here a bit visible.
What do you see from top:
- Temperature from my balkony sensor
- Temperature from my livingroom sensor
- Todays forecast from weather.home (HA default forecast)
- Forcast if it will rain the next 30min. See Used tutorials
- Check if there is a switch/light still on
- When the next garbage collection will take place. See Used tutorials
What i did:
- Wrote the code in espHome
- Implemented a Sensor in the HA config file
- Implemented a dimmable light in HA for dimming the TFT backlight
- Mounted the the TTGO T4 on some carton
- Glued the mirror foil on the picture frame glass
- Mounted everything together
Here my code from espHome:
esphome:
name: smartmirror
esp32: # LILYGO TTGO T4 V1.3 ILI9341
board: ttgo-t1
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
ota:
password: "2e416c3d54841a0eb0188d1b602679df"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Smartmirror Fallback Hotspot"
password: "8uM7SpkT1K8q"
captive_portal:
time:
- platform: homeassistant
id: homeassistant_time
spi: #intializing Display
clk_pin: 18 # (Pin on display - SCK/T_CLK)
mosi_pin: 23 # (Pin on display - SDI(MOSI)/T_DIN)
miso_pin: 12 # (Pin on display - SDO(MISO)/T_DO)
display:
- platform: ili9341 #intializing Display
model: TFT 2.4 #Resolution 320x240
rotation: 270
cs_pin: 27 # (Pin on display - CS)
dc_pin: 32 # (Pin on display - DC)
led_pin: 4 # (Pin on display - LED)
reset_pin: 14 # (Pin on display - RESET)
lambda: |-
//layout
//top line
it.filled_circle(25, 25, 25, yellow);
it.filled_rectangle(27, 0, 185, 23, yellow);
it.filled_circle(308, 11, 11, yellow);
it.filled_rectangle(296, 0, 9, 23, yellow);
//vertikal line I
it.filled_rectangle(0, 29, 50, 22, yellow);
it.line(51, 23, 51, 27, yellow);
it.line(52, 23, 52, 25, yellow);
it.draw_pixel_at(53, 24, yellow);
it.line(53, 23, 56, 23, yellow);
it.filled_rectangle(0, 54, 50, 48, blue);
//mid line I
it.filled_rectangle(0, 105, 50, 30, rosa);
it.filled_circle(7, 136, 7, rosa);
it.filled_rectangle(9, 131, 177, 13, rosa);
it.filled_rectangle(186, 131, 112, 6, rosa);
it.filled_rectangle(298, 131, 12, 13, rosa);
it.filled_circle(312, 137, 6, rosa);
it.line(50, 130, 50, 124, rosa);
it.line(51, 130, 51, 127, rosa);
it.line(52, 130, 52, 128, rosa);
it.draw_pixel_at(53, 129, rosa);
it.line(53, 130, 56, 130, rosa);
//mid line II
it.filled_rectangle(0, 158, 50, 28, grey);
it.filled_circle(7, 156, 7, grey);
it.filled_rectangle(9, 149, 177, 13, grey);
it.filled_rectangle(186, 157, 112, 5, grey);
it.filled_rectangle(298, 149, 12, 13, grey);
it.filled_circle(312, 155, 6, grey);
it.line(50, 162, 50, 168, grey);
it.line(51, 162, 51, 166, grey);
it.line(52, 162, 52, 164, grey);
it.draw_pixel_at(53, 163, grey);
it.line(53, 162, 56, 162, grey);
//buttom line
it.filled_rectangle(0, 189, 50, 21, yellow);
it.filled_circle(25, 214, 25, yellow);
it.filled_rectangle(30, 217, 275, 23, yellow);
it.filled_circle(308, 228, 11, yellow);
it.line(51, 216, 51, 212, yellow);
it.line(52, 216, 52, 214, yellow);
it.draw_pixel_at(53, 215, yellow);
it.line(53, 216, 56, 216, yellow);
//Data
it.print(217, 0, id(OkudaBold), red, "Guten Tag!");
it.print(7, 85, id(Okuda15), "Weather");
it.printf(70, 25, id(Okuda), "Temp. Balkon: %.1f °C", id(temp_balkon).state);
it.printf(70, 50, id(Okuda), "Temp. Wohnzimmer: %.1f °C", id(temp_wohnzimmer).state);
it.printf(70, 75, id(Okuda), "Wetter heute: %s", id(wetter).state.c_str());
it.printf(70, 100, id(Okuda), "%s in den nachsten 30min.", id(regen30min).state.c_str());
it.strftime(193, 133, id(OkudaBold), yellow2, "%H:%M %d.%m.%y", id(homeassistant_time).now());
it.printf(70, 161, id(Okuda), "Alles Licht aus: %s", id(lightcheck).state.c_str());
it.printf(70, 186, id(Okuda), "Gelber Sack in %s", id(nachster_abfall).state.c_str());
it.print(11, 188, id(Okuda15), "Status");
font:
- file: "Okuda.ttf" #normal
id: Okuda
size: 25
- file: "OkudaBold.ttf" #fett
id: OkudaBold
size: 25
- file: "Okuda.ttf" #normal
id: Okuda15
size: 15
color:
- id: red
red: 100%
green: 0%
blue: 0%
- id: rosa
red_int: 204
green_int: 154
blue_int: 204
white_int: 0
- id: blue
red_int: 150
green_int: 160
blue_int: 255
white_int: 0
- id: grey
red_int: 180
green_int: 178
blue_int: 180
white_int: 0
- id: yellow
red_int: 212
green_int: 164
blue_int: 114
white_int: 0
- id: yellow2
red_int: 240
green_int: 196
blue_int: 19
white_int: 0
# Define a PWM output on the ESP32
output:
- platform: ledc
pin: 4
id: gpio_4_backlight_pwm
# Define a monochromatic, dimmable light for the backlight. Such light is later controllable via HA dashboard
light:
- platform: monochromatic
output: gpio_4_backlight_pwm
name: "Display Backlight"
id: back_light
sensor:
- platform: homeassistant
id: temp_balkon
entity_id: sensor.temperature_5
- platform: homeassistant
id: temp_wohnzimmer
entity_id: sensor.temperature_2
text_sensor:
- platform: homeassistant
id: wetter
entity_id: weather.home
filters:
- substitute:
- "sunny -> sonnig"
- "partlycloudy -> teilweise wolkig"
- "rainy -> Regen"
- "windy -> windig"
- "snowy -> Schnee"
- "lightning -> Gewitter"
- "cloudy -> wolkig"
- platform: homeassistant
id: lightcheck
entity_id: light.alle_lichter
filters:
- substitute:
- "on -> nein"
- "off -> ja"
- platform: homeassistant #https://www.youtube.com/watch?v=SoBjbW4zjWs
id: nachster_abfall
entity_id: sensor.mullabfuhr_wann
filters:
- substitute:
- "1 -> morgen"
- "2 -> ubermorgen"
- "3 -> drei Tagen"
- "4 -> vier Tagen"
- "5 -> funf Tagen"
- "6 -> sechs Tagen"
- "7 -> naechste Woche"
- "8 -> naechste Woche"
- "9 -> naechste Woche"
- "10 -> naechste Woche"
- "11 -> naechste Woche"
- "12 -> naechste Woche"
- "13 -> naechste Woche"
- "14 -> naechste Woche"
- platform: homeassistant #https://www.ajfriesen.com/rain-warning-sensor-with-home-assistant/
id: regen30min
entity_id: sensor.rrrainin30min
filters:
- substitute:
- "1 -> Es regnet"
- "0 -> Kein Regen"
Config.yaml code:
- sensor: #Sensor zur detektion in wie vielen Tagen der nächste Eintrag im Müllabfuhr Kalender ist und wie dieser heist
- unique_id: mullabfuhr_template #https://www.youtube.com/watch?v=SoBjbW4zjWs
name: mullabfuhr_wann
state: "{{(((as_timestamp(state_attr('calendar.mullabfuhr', 'start_time')))-as_timestamp(now())) | int /60/1440) | round(0,'ceil')}}"
#state: "{{ state_attr('calendar.mullabfuhr','message')~'|'~(((as_timestamp(state_attr('calendar.mullabfuhr', 'start_time')))-as_timestamp(now())) | int /60/1440) | round(0,'ceil')}}"
icon: mdi:trash-can
sensor: # Sensor zur detektion ob es in 30min,60min oder 120min regnet.
- platform: rest #https://www.ajfriesen.com/rain-warning-sensor-with-home-assistant/
name: regenradar
scan_interval: 300
json_attributes:
- raintext
- rainin30min
- rainin60min
- rainin120min
resource: https://morgenwirdes.de/api/v3/rain.php?lat=XX&long=YY
value_template: "{{ value_json.raintext }}"
- platform: template
sensors:
rrraintext:
friendly_name: "Vorhersage"
value_template: "{{ state_attr('sensor.regenradar', 'raintext') }}"
rrrainin30min:
friendly_name: "Regen in 30min"
value_template: "{{ state_attr('sensor.regenradar', 'rainin30min') }}"
rrrainin60min:
friendly_name: "Regen in 60min"
value_template: "{{ state_attr('sensor.regenradar', 'rainin60min') }}"
rrrainin120min:
friendly_name: "Regen in 120min"
value_template: "{{ state_attr('sensor.regenradar', 'rainin120min') }}"
Used tutorials:
- How to make events in the local HA calendar and present them in espHome LINK
- How to implement an Rain Forcast for the next 30-120min LINK
- a comparable project LINK
Future plans:
- implement deepsleep with a wake up from a PIR
- a way more sofisticated mirror