Last active
August 6, 2025 18:55
-
-
Save dariusk/25b4d73319d793981c9100a0e32831bf to your computer and use it in GitHub Desktop.
ESPHome e-ink dashboard with weather, tri-met transit, and cute art
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
| # This isn't my whole configuration.yaml, just the parts that I dropped into my existing | |
| # configuration.yaml to get the dashboard working -Darius | |
| # For Portland-area transit stuff, I use the Trimet developer API. You can get the API key ("appId") here: https://developer.trimet.org/appid/registration/ | |
| sensor: | |
| - platform: rest | |
| name: Trimet 20 E 0 | |
| resource: "https://developer.trimet.org/ws/V2/arrivals?locIDs=813&appID=APP_DEV_KEY_GOES_HERE" | |
| scan_interval: 240 | |
| method: GET | |
| json_attributes_path: '$.resultSet' | |
| json_attributes: 'arrival' | |
| value_template: > | |
| {{ iif(now() > (((value_json.resultSet.arrival[0].estimated | int) / 1000) | timestamp_local | as_datetime),'Now',time_until(((value_json.resultSet.arrival[0].estimated | int) / 1000) | timestamp_local | as_datetime, 1)) }} | |
| - platform: rest | |
| name: Trimet 20 E 1 | |
| resource: "https://developer.trimet.org/ws/V2/arrivals?locIDs=813&appID=APP_DEV_KEY_GOES_HERE" | |
| scan_interval: 240 | |
| method: GET | |
| json_attributes_path: '$.resultSet' | |
| json_attributes: 'arrival' | |
| value_template: > | |
| {{ time_until(((value_json.resultSet.arrival[1].estimated | int) / 1000) | timestamp_local | as_datetime, 1) }} | |
| - platform: rest | |
| name: Trimet 20 W 0 | |
| resource: "https://developer.trimet.org/ws/V2/arrivals?locIDs=814&appID=APP_DEV_KEY_GOES_HERE" | |
| scan_interval: 240 | |
| method: GET | |
| json_attributes_path: '$.resultSet' | |
| json_attributes: 'arrival' | |
| value_template: > | |
| {{ iif(now() > (((value_json.resultSet.arrival[0].estimated | int) / 1000) | timestamp_local | as_datetime),'Now',time_until(((value_json.resultSet.arrival[0].estimated | int) / 1000) | timestamp_local | as_datetime, 1)) }} | |
| - platform: rest | |
| name: Trimet 20 W 1 | |
| resource: "https://developer.trimet.org/ws/V2/arrivals?locIDs=814&appID=APP_DEV_KEY_GOES_HERE" | |
| scan_interval: 240 | |
| method: GET | |
| json_attributes_path: '$.resultSet' | |
| json_attributes: 'arrival' | |
| value_template: > | |
| {{ time_until(((value_json.resultSet.arrival[1].estimated | int) / 1000) | timestamp_local | as_datetime, 1) }} | |
| - platform: rest | |
| name: Trimet 72 N 0 | |
| resource: "https://developer.trimet.org/ws/V2/arrivals?locIDs=7936&appID=APP_DEV_KEY_GOES_HERE" | |
| scan_interval: 240 | |
| method: GET | |
| json_attributes_path: '$.resultSet' | |
| json_attributes: 'arrival' | |
| value_template: > | |
| {{ iif(now() > (((value_json.resultSet.arrival[0].estimated | int) / 1000) | timestamp_local | as_datetime),'Now',time_until(((value_json.resultSet.arrival[0].estimated | int) / 1000) | timestamp_local | as_datetime, 1)) }} | |
| - platform: rest | |
| name: Trimet 72 N 1 | |
| resource: "https://developer.trimet.org/ws/V2/arrivals?locIDs=7936&appID=APP_DEV_KEY_GOES_HERE" | |
| scan_interval: 240 | |
| method: GET | |
| json_attributes_path: '$.resultSet' | |
| json_attributes: 'arrival' | |
| value_template: > | |
| {{ time_until(((value_json.resultSet.arrival[1].estimated | int) / 1000) | timestamp_local | as_datetime, 1) }} | |
| - platform: rest | |
| name: Trimet 72 S 0 | |
| resource: "https://developer.trimet.org/ws/V2/arrivals?locIDs=7937&appID=APP_DEV_KEY_GOES_HERE" | |
| scan_interval: 240 | |
| method: GET | |
| json_attributes_path: '$.resultSet' | |
| json_attributes: 'arrival' | |
| value_template: > | |
| {{ iif(now() > (((value_json.resultSet.arrival[0].estimated | int) / 1000) | timestamp_local | as_datetime),'Now',time_until(((value_json.resultSet.arrival[0].estimated | int) / 1000) | timestamp_local | as_datetime, 1)) }} | |
| - platform: rest | |
| name: Trimet 72 S 1 | |
| resource: "https://developer.trimet.org/ws/V2/arrivals?locIDs=7937&appID=APP_DEV_KEY_GOES_HERE" | |
| scan_interval: 240 | |
| method: GET | |
| json_attributes_path: '$.resultSet' | |
| json_attributes: 'arrival' | |
| value_template: > | |
| {{ time_until(((value_json.resultSet.arrival[1].estimated | int) / 1000) | timestamp_local | as_datetime, 1) }} | |
| template: | |
| # I just set this sensor to "on" all the time, see notes in dashboard.yaml | |
| - binary_sensor: | |
| - name: Weatherman Motion Detected | |
| unique_id: "dfa78de7-d761-425f-9731-86f1af332eac" | |
| device_class: "occupancy" | |
| delay_off: 2min | |
| state: >- | |
| on | |
| # Bundle up all the data to send over to Weatherman (ESPHome device). | |
| # I set this to every two minutes -Darius | |
| - trigger: | |
| platform: time_pattern | |
| minutes: "/2" | |
| sensor: | |
| - name: Weatherman Data | |
| state: "OK" | |
| attributes: | |
| bus_status_burnside_e_0: > | |
| {{ states('sensor.trimet_20_e_0') | upper }} | |
| bus_status_burnside_e_1: > | |
| {{ states('sensor.trimet_20_e_1') | upper }} | |
| bus_status_burnside_w_0: > | |
| {{ states('sensor.trimet_20_w_0') | upper }} | |
| bus_status_burnside_w_1: > | |
| {{ states('sensor.trimet_20_w_1') | upper }} | |
| bus_status_trimet_72_n_0: > | |
| {{ states('sensor.trimet_72_n_0') | upper }} | |
| bus_status_trimet_72_n_1: > | |
| {{ states('sensor.trimet_72_n_1') | upper }} | |
| bus_status_trimet_72_s_0: > | |
| {{ states('sensor.trimet_72_s_0') | upper }} | |
| bus_status_trimet_72_s_1: > | |
| {{ states('sensor.trimet_72_s_1') | upper }} | |
| weather_condition_now: > | |
| {{ states('sensor.pirateweather_icon_0h') }} | |
| weather_condition_0: > | |
| {{ states('sensor.pirateweather_icon_0h') }} | |
| weather_temperature_0: > | |
| {{ states('sensor.pirateweather_temperature_0h') }} | |
| weather_timestamp_0: > | |
| {{ as_timestamp(now() + timedelta( hours = 0 )) | timestamp_custom('%-I %p') }} | |
| weather_condition_1: > | |
| {{ states('sensor.pirateweather_icon_3h') }} | |
| weather_temperature_1: > | |
| {{ states('sensor.pirateweather_temperature_3h') }} | |
| weather_timestamp_1: > | |
| {{ as_timestamp(now() + timedelta( hours = 3 )) | timestamp_custom('%-I %p') }} | |
| weather_condition_2: > | |
| {{ states('sensor.pirateweather_icon_6h') }} | |
| weather_temperature_2: > | |
| {{ states('sensor.pirateweather_temperature_6h') }} | |
| weather_timestamp_2: > | |
| {{ as_timestamp(now() + timedelta( hours = 6 )) | timestamp_custom('%-I %p') }} | |
| weather_condition_3: > | |
| {{ states('sensor.pirateweather_icon_9h') }} | |
| weather_temperature_3: > | |
| {{ states('sensor.pirateweather_temperature_9h') }} | |
| weather_timestamp_3: > | |
| {{ as_timestamp(now() + timedelta( hours = 9 )) | timestamp_custom('%-I %p') }} |
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
| # WEATHERMAN DASHBOARD | |
| # For Home Assistant and ESPHome | |
| # Designed by Madelena Mak 2022 - https://mmak.es | |
| # Modified by Darius Kazemi 2025 | |
| # Cue "Blame it on the Weatherman" by B*Witched! | |
| esphome: | |
| name: "weatherman" | |
| on_boot: | |
| priority: 200.0 | |
| then: | |
| - component.update: eink_display | |
| - wait_until: | |
| condition: | |
| lambda: 'return id(data_updated) == true;' | |
| # Wait a bit longer so all the items are received | |
| - delay: 5s | |
| - logger.log: "Initial sensor data received: Refreshing display..." | |
| - lambda: 'id(initial_data_received) = true;' | |
| - script.execute: update_screen | |
| esp32: | |
| board: esp32dev | |
| framework: | |
| type: arduino | |
| # Enable logging | |
| logger: | |
| # Enable Home Assistant API | |
| api: | |
| # The following `platform` listing is a fix required after HA updates in July 2025 | |
| # https://esphome.io/changelog/2025.7.0#web-server-ota-platform | |
| ota: | |
| - platform: esphome | |
| - platform: web_server | |
| button: | |
| - platform: shutdown | |
| name: "Weatherman - Shutdown" | |
| - platform: restart | |
| name: "Weatherman - Restart" | |
| - platform: template | |
| name: "Weatherman - Refresh Screen" | |
| entity_category: config | |
| on_press: | |
| - script.execute: update_screen | |
| # Global variables for detecting if the display needs to be refreshed. (Thanks @paviro!) | |
| globals: | |
| - id: data_updated | |
| type: bool | |
| restore_value: no | |
| initial_value: 'false' | |
| - id: initial_data_received | |
| type: bool | |
| restore_value: no | |
| initial_value: 'false' | |
| - id: recorded_display_refresh | |
| type: int | |
| restore_value: yes | |
| initial_value: '0' | |
| # Script for updating screen - Refresh display and publish refresh count and time. (Thanks @paviro!) | |
| script: | |
| - id: update_screen | |
| then: | |
| - lambda: 'id(data_updated) = false;' | |
| - component.update: eink_display | |
| - lambda: 'id(recorded_display_refresh) += 1;' | |
| - lambda: 'id(display_last_update).publish_state(id(homeassistant_time).now().timestamp);' | |
| # Check whether the display needs to be refreshed every two minutes, | |
| # based on whether new data is received or motion is detected. (Thanks @paviro!) | |
| time: | |
| - platform: homeassistant | |
| id: homeassistant_time | |
| on_time: | |
| - seconds: 0 | |
| minutes: /2 | |
| then: | |
| - if: | |
| condition: | |
| lambda: 'return id(data_updated) == true;' | |
| then: | |
| - if: | |
| condition: | |
| binary_sensor.is_on: motion_detected | |
| then: | |
| - logger.log: "Sensor data updated and activity in home detected: Refreshing display..." | |
| - script.execute: update_screen | |
| else: | |
| - logger.log: "Sensor data updated but no activity in home - skipping display refresh." | |
| else: | |
| - logger.log: "No sensors updated - skipping display refresh." | |
| # Wifi information | |
| wifi: | |
| ssid: SSID_GOES_HERE | |
| password: PASSWORD_GOES_HERE | |
| # Enable fallback hotspot (captive portal) in case wifi connection fails. These values can be anything you want, they are | |
| # for a temporary access point this will set up (I think?? -Darius) | |
| ap: | |
| ssid: MAKE_UP_AN_SSID | |
| password: MAKE_UP_A_PASSWORD | |
| # I did not bother optimizing the glyphs here, there are probably some that can be removed from the title fonts | |
| # but my device isn't complaining so I'm not bothering. -Darius | |
| # Include custom fonts | |
| font: | |
| - file: 'fonts/GothamRnd-Book.ttf' | |
| id: font_small_book | |
| size: 18 | |
| - file: 'fonts/GothamRnd-Bold.ttf' | |
| id: font_large_bold | |
| size: 108 | |
| glyphs: [' ', '-', '°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'F'] | |
| - file: 'fonts/GothamRnd-Bold.ttf' | |
| id: font_title | |
| size: 54 | |
| glyphs: ['P', 'O', 'T', 'H', 'R', 'L', 'I', 'N', ' ', 'M', 'E'] | |
| - file: 'fonts/GothamRnd-Bold.ttf' | |
| id: font_medium_bold | |
| size: 30 | |
| # glyphs: [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'M', 'I', 'N'] | |
| - file: 'fonts/GothamRnd-Bold.ttf' | |
| id: font_small_bold | |
| size: 24 | |
| # glyphs: [' ', '-', '°', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', 'M', 'I', 'N'] | |
| # Include Material Design Icons font | |
| # Thanks to https://community.home-assistant.io/t/display-materialdesign-icons-on-esphome-attached-to-screen/199790/16 | |
| # Note: my weather app only supports a small number of the weather states covered by these glyphs. I could optimize them | |
| # to only be the subset I need and save a little memory on device, but also it doesn't matter for this device. -Darius | |
| - file: 'fonts/materialdesignicons-webfont.ttf' | |
| id: font_mdi_large | |
| size: 96 | |
| 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 | |
| - file: 'fonts/materialdesignicons-webfont.ttf' | |
| id: font_mdi_medium | |
| size: 44 | |
| glyphs: *mdi-weather-glyphs | |
| # This is an image file I made that is a transparent PNG with some art on it from the game Deltarune. Just for fun. | |
| image: | |
| - file: 'images/dr.png' | |
| type: BINARY | |
| id: 'deltarune' | |
| # Check if motion is detected in the living room. | |
| # Note: I set this sensor to be "ON" all the time since I don't actually own a motion sensor. The reason | |
| # there's a motion sensor at all is because you don't want to update the display every minute of every day, | |
| # because the display itself is only rated for a few million refreshes. Every refresh you save extends device life. | |
| # So the original implementation only updates the display when motion is detected nearby. What I did instead | |
| # was connect the display to a smart power plug that turns off from 11pm to 6am when I don't really need it. -Darius | |
| binary_sensor: | |
| - platform: homeassistant | |
| entity_id: binary_sensor.weatherman_motion_detected | |
| id: motion_detected | |
| sensor: | |
| # Create sensors for monitoring Weatherman remotely. | |
| - platform: template | |
| name: "Weatherman - Display Last Update" | |
| device_class: timestamp | |
| entity_category: "diagnostic" | |
| id: display_last_update | |
| - platform: template | |
| name: "Weatherman - Recorded Display Refresh" | |
| accuracy_decimals: 0 | |
| unit_of_measurement: "Refreshes" | |
| state_class: "total_increasing" | |
| entity_category: "diagnostic" | |
| lambda: 'return id(recorded_display_refresh);' | |
| - platform: wifi_signal | |
| name: "Weatherman - WiFi Signal Strength" | |
| id: wifisignal | |
| unit_of_measurement: "dBm" | |
| entity_category: "diagnostic" | |
| update_interval: 60s | |
| # Call Transit and Weather sensors from HA. | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_temperature_0 | |
| id: weather_temperature_0 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_temperature_1 | |
| id: weather_temperature_1 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_temperature_2 | |
| id: weather_temperature_2 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_temperature_3 | |
| id: weather_temperature_3 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| text_sensor: | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_condition_now | |
| id: weather_condition_now | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_condition_0 | |
| id: weather_condition_0 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_timestamp_0 | |
| id: weather_timestamp_0 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_condition_1 | |
| id: weather_condition_1 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_timestamp_1 | |
| id: weather_timestamp_1 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_condition_2 | |
| id: weather_condition_2 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_timestamp_2 | |
| id: weather_timestamp_2 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_condition_3 | |
| id: weather_condition_3 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: weather_timestamp_3 | |
| id: weather_timestamp_3 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: bus_status_burnside_e_0 | |
| id: bus_status_burnside_e_0 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: bus_status_burnside_e_1 | |
| id: bus_status_burnside_e_1 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: bus_status_burnside_w_0 | |
| id: bus_status_burnside_w_0 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: bus_status_burnside_w_1 | |
| id: bus_status_burnside_w_1 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: bus_status_trimet_72_n_0 | |
| id: bus_status_trimet_72_n_0 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: bus_status_trimet_72_n_1 | |
| id: bus_status_trimet_72_n_1 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: bus_status_trimet_72_s_0 | |
| id: bus_status_trimet_72_s_0 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| - platform: homeassistant | |
| entity_id: sensor.weatherman_data | |
| attribute: bus_status_trimet_72_s_1 | |
| id: bus_status_trimet_72_s_1 | |
| on_value: | |
| then: | |
| - lambda: 'id(data_updated) = true;' | |
| # Define colors | |
| # This design is white on black so this is necessary. | |
| color: | |
| - id: color_bg | |
| red: 0% | |
| green: 0% | |
| blue: 0% | |
| white: 0% | |
| - id: color_text | |
| red: 0% | |
| green: 0% | |
| blue: 0% | |
| white: 100% | |
| # Pins for Waveshare ePaper ESP Board | |
| spi: | |
| clk_pin: GPIO13 | |
| mosi_pin: GPIO14 | |
| # Now render everything on the ePaper screen. | |
| display: | |
| - platform: waveshare_epaper | |
| id: eink_display | |
| cs_pin: GPIO15 | |
| dc_pin: GPIO27 | |
| busy_pin: | |
| number: GPIO25 | |
| inverted: true | |
| reset_pin: GPIO26 | |
| reset_duration: 2ms | |
| model: 7.50inV2 | |
| update_interval: never | |
| rotation: 90° | |
| lambda: |- | |
| // 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"}, | |
| {"sleet", "\U000F0592"}, | |
| {"hazy", "\U000F0F30"}, | |
| {"hurricane", "\U000F0898"}, | |
| {"lightning", "\U000F0593"}, | |
| {"lightning-rainy", "\U000F067E"}, | |
| {"clear-night", "\U000F0594"}, | |
| {"partly-cloudy-night", "\U000F0F31"}, | |
| {"partly-cloudy-day", "\U000F0595"}, | |
| {"partly-lightning", "\U000F0F32"}, | |
| {"partly-rainy", "\U000F0F33"}, | |
| {"partly-snowy", "\U000F0F34"}, | |
| {"partly-snowy-rainy", "\U000F0F35"}, | |
| {"pouring", "\U000F0596"}, | |
| {"rain", "\U000F0597"}, | |
| {"snow", "\U000F0598"}, | |
| {"snowy-heavy", "\U000F0F36"}, | |
| {"snowy-rainy", "\U000F067F"}, | |
| {"clear-day", "\U000F0599"}, | |
| {"sunny-alert", "\U000F0F37"}, | |
| {"sunny-off", "\U000F14E4"}, | |
| {"sunset", "\U000F059A"}, | |
| {"sunset-down", "\U000F059B"}, | |
| {"sunset-up", "\U000F059C"}, | |
| {"tornado", "\U000F0F38"}, | |
| {"wind", "\U000F059D"}, | |
| {"windy-variant", "\U000F059E"}, | |
| }; | |
| // Show loading screen before data is received. | |
| if (id(initial_data_received) == false) { | |
| it.printf(240, 390, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "WAITING FOR DATA..."); | |
| } else { | |
| // Weather Section | |
| it.printf(100, 158-114, id(font_mdi_large), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_now).state.c_str()].c_str()); | |
| it.printf(300, 158-114, id(font_large_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°F", id(weather_temperature_0).state); | |
| it.printf(105, 282-104, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_0).state.c_str()); | |
| it.printf(105, 306-104, id(font_mdi_medium), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_0).state.c_str()].c_str()); | |
| it.printf(105, 354-104, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°F", id(weather_temperature_0).state); | |
| it.printf(195, 282-104, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_1).state.c_str()); | |
| it.printf(195, 306-104, id(font_mdi_medium), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_1).state.c_str()].c_str()); | |
| it.printf(195, 354-104, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°F", id(weather_temperature_1).state); | |
| it.printf(285, 282-104, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_2).state.c_str()); | |
| it.printf(285, 306-104, id(font_mdi_medium), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_2).state.c_str()].c_str()); | |
| it.printf(285, 354-104, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°F", id(weather_temperature_2).state); | |
| it.printf(375, 282-104, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(weather_timestamp_3).state.c_str()); | |
| it.printf(375, 306-104, id(font_mdi_medium), color_text, TextAlign::TOP_CENTER, "%s", weather_icon_map[id(weather_condition_3).state.c_str()].c_str()); | |
| it.printf(375, 354-104, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%2.0f°F", id(weather_temperature_3).state); | |
| // Transit section, shows Portland bus arrival times from some custom sensors I set up previously | |
| it.print(125, 330, id(font_medium_bold), color_text, TextAlign::TOP_CENTER, "20 W"); | |
| it.printf(125, 372, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(bus_status_burnside_w_0).state.c_str()); | |
| it.printf(125, 405, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(bus_status_burnside_w_1).state.c_str()); | |
| it.print(355, 330, id(font_medium_bold), color_text, TextAlign::TOP_CENTER, "20 E"); | |
| it.printf(355, 372, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(bus_status_burnside_e_0).state.c_str()); | |
| it.printf(355, 405, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(bus_status_burnside_e_1).state.c_str()); | |
| it.print(125, 430+32, id(font_medium_bold), color_text, TextAlign::TOP_CENTER, "72 N"); | |
| it.printf(125, 472+32, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(bus_status_trimet_72_n_0).state.c_str()); | |
| it.printf(125, 505+32, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(bus_status_trimet_72_n_1).state.c_str()); | |
| it.print(355, 430+32, id(font_medium_bold), color_text, TextAlign::TOP_CENTER, "72 S"); | |
| it.printf(355, 472+32, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(bus_status_trimet_72_s_0).state.c_str()); | |
| it.printf(355, 505+32, id(font_small_bold), color_text, TextAlign::TOP_CENTER, "%s", id(bus_status_trimet_72_s_1).state.c_str()); | |
| // Cute image of Lanino and Elnina! | |
| it.image(75, 610, id(deltarune)); | |
| // Refresh Timestamp | |
| // Code by EnsconcE from https://community.home-assistant.io/t/esphome-show-time/348903 | |
| char str[17]; | |
| time_t currTime = id(homeassistant_time).now().timestamp; | |
| strftime(str, sizeof(str), "%H:%M", localtime(&currTime)); | |
| it.printf(0, 0, id(font_small_book), color_text, TextAlign::TOP_LEFT, "REFRESHED AT %s", str); | |
| } | |
| captive_portal: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment