so it appears that in 2023.4.2 the todoist integration is broken again. I’ve done a restore, back to 2023.3.6 but now I cant get access to the whole config folder (It appears it only did a partial backup before the last update). Ill have to restore back to the broken todoist version to get everything working. But hopefully this is enough info FML lol
- platform: template
sensors:
esphome_to_do:
friendly_name: "ESPHome Alexa To-Do"
value_template: '{{states.calendar.alexa_to_do_list.attributes.all_tasks | replace(''['','''') | replace('']'','''') | replace("''","")}}'
- platform: template
sensors:
epshome_shopping_list:
friendly_name: "ESPHome Alexa Shopping List"
value_template: '{{states.calendar.alexa_shopping_list.attributes.all_tasks | replace(''['','''') | replace('']'','''') | replace("''","") }}'
So I have a value template for each that removes brackets , and the ’ and outputs that to a new entity that the ESPhome.yaml uses, looks like
As you can see we have “stuff 1” though “stuff 4” but when looking at the actual display the first entry is missing, the order is also reversed, but that does not matter (you can see this for both the to do list and the shopping list).
And lastly here is the code I have in the esphome.yaml. I’ve just sanitized it a little, but everything important is there.
esphome:
name: to-do
on_boot:
priority: 200.0
then:
- component.update: eink_display
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# 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'
# Include custom fonts
font:
- file: fonts/materialdesignicons-webfont.ttf
id: icon_font
size: 50
glyphs:
- "\U000F0590" # weather-cloudy
- "\U000F0F2F" # weather-cloudy-alert
- "\U000F0E6E" # weather-cloudy-arrow-right
- "\U000F0591" # weather-fog
- "\U000F0592" # weather-hail
- "\U000F0F30" # weather-hazy
- "\U000F0898" # weather-hurricane
- "\U000F0593" # weather-lightning
- "\U000F067E" # weather-lightning-rainy
- "\U000F0594" # weather-night
- "\U000F0F31" # weather-night-partly-cloudy
- "\U000F0595" # weather-partly-cloudy
- "\U000F0F32" # weather-partly-lightning
- "\U000F0F33" # weather-partly-rainy
- "\U000F0F34" # weather-partly-snowy
- "\U000F0F35" # weather-partly-snowy-rainy
- "\U000F0596" # weather-pouring
- "\U000F0597" # weather-rainy
- "\U000F0598" # weather-snowy
- "\U000F0F36" # weather-snowy-heavy
- "\U000F067F" # weather-snowy-rainy
- "\U000F0599" # weather-sunny
- "\U000F0F37" # weather-sunny-alert
- "\U000F14E4" # weather-sunny-off
- "\U000F059A" # weather-sunset
- "\U000F059B" # weather-sunset-down
- "\U000F059C" # weather-sunset-up
- "\U000F0F38" # weather-tornado
- "\U000F059D" # weather-windy
- "\U000F059E" # weather-windy-variant
- "\U000F050F" # mdi-thermometer
- "\U000F18D7" # sun-thermometer-outline
- "\U000F0F55" # home-thermometer-outline
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_title
size: 40
glyphs:
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '/','[', ']',"'"]
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_small_bold
size: 25
glyphs:
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '/','[', ']',"'"]
# Check whether the display needs to be refreshed every minute,
# based on whether new data is received or motion is detected. (Thanks @paviro!)
time:
- platform: homeassistant
id: homeassistant_time
on_time:
- seconds: 0
minutes: /1
then:
- if:
condition:
lambda: 'return id(data_updated) == true;'
then:
- lambda: 'id(initial_data_received) = true;'
- if:
condition:
binary_sensor.is_on: motion_detected
then:
- logger.log: "Sensor data updated and activity in home detected: Refreshing display..."
- component.update: eink_display
- lambda: 'id(data_updated) = false;'
else:
- logger.log: "Sensor data updated but no activity in home - skipping display refresh."
- platform: sntp
id: ntp
timezone: Australia/Sydney
servers:
- 0.pool.ntp.org
- 1.pool.ntp.org
- 2.pool.ntp.org
# Check if motion is detected in the bathroom.
binary_sensor:
- platform: homeassistant
entity_id: binary_sensor.bathroom_motion_sensor
id: motion_detected
# Call calender sensors from HA.
text_sensor:
- platform: homeassistant
entity_id: sensor.esphome_to_do
id: alexa_to_do_list
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.epshome_shopping_list
id: alexa_shopping_list
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
name: "Today Weather"
entity_id: weather.home
id: weather_icon
internal: true
sensor:
- platform: homeassistant
entity_id: sensor.outside_temperature_sensor
id: weather_temperature
on_value:
then:
- lambda: 'id(data_updated) = true;'
- platform: homeassistant
entity_id: sensor.bedroom_temperature
id: bedroom_temp
on_value:
then:
- lambda: 'id(data_updated) = true;'
# Define colors
# This design is white on black so this is necessary.
color:
- id: color_black
red: 0%
green: 0%
blue: 0%
white: 50%
- id: color_white
red: 0%
green: 0%
blue: 0%
white: 0%
- id: color_red
red: 0%
green: 0%
blue: 0%
white: 0%
# 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: GPIO25
reset_pin: GPIO26
reset_duration: 2ms
model: 7.50in-bV2
update_interval: 1h
rotation: 90°
lambda: |-
// Fill background.
// it.fill(color_bg);
// Show loading screen before data is received.
if (id(initial_data_received) == false) {
it.printf(215, 190, id(font_title), color_black, TextAlign::TOP_CENTER, "Just Wait a Second");
it.printf(200, 410, id(font_title), color_black, TextAlign::TOP_CENTER, "It's Still Loading");
} else {
int time = id(ntp).now().hour * 100 + id(ntp).now().minute;
if (id(weather_icon).has_state()) {
std::map<std::string, std::string> weather_state {
{ "sunny", "\U000F0599" }, // mdi:weather-sunny
{ "clear-night", "\U000F0594" }, // mdi:weather-night
{ "cloudy", "\U000F0590"}, // mdi:weather-cloudy
{ "rainy", "\U000F0597" }, // mdi:weather-pouring
{ "windy", "\U000F059D" }, // mdi:weather-windy-variant
{ "fog", "\U000F0591" }, // mdi:weather-fog
{ "partlycloudy", "\U000F0595" }, // mdi:weather-partly-cloudy
};
if (time < 1900) {
it.printf(20, 90, id(icon_font), TextAlign::BASELINE_LEFT, weather_state[id(weather_icon).state.c_str()].c_str());
} else {
it.printf(20, 90, id(icon_font), TextAlign::BASELINE_LEFT, weather_state[id(weather_icon).state.c_str()].c_str());
}
}
// Outside Temp
it.printf(80, 90, id(icon_font), TextAlign::BASELINE_LEFT, "\U000F18D7");
it.printf(135, 85, id(font_title), color_black, TextAlign::BASELINE_LEFT, "%2.0f°", id(weather_temperature).state);
// Inside Temp
it.printf(240, 90, id(icon_font), TextAlign::BASELINE_LEFT, "\U000F0F55");
it.printf(295, 85, id(font_title), color_black, TextAlign::BASELINE_LEFT, "%2.0f°", id(bedroom_temp).state);
// To Do List
it.printf(20, 150, id(font_title), color_black, TextAlign::BASELINE_LEFT, "To Do List");
it.line(240, 135, 480, 135);
std::string str1 = id(alexa_to_do_list).state;
// Display position and line height
int xPos1 = 20, yPos1 = 190, lineHeight1 = 20;
// Split into lines where comma is delimiter
std::size_t curPos1 = 0, commaPos1 = 0;
while ((commaPos1 = str1.find(',', curPos1)) != std::string::npos) {
it.print(xPos1, yPos1, id(font_small_bold), str1.substr(curPos1, commaPos1 - curPos1).c_str());
yPos1 += lineHeight1;
curPos1 = commaPos1 + 1;
}
// Shopping List
it.printf(20, 408, id(font_title), color_black, TextAlign::BASELINE_LEFT, "Shopping List");
it.line(320, 395, 480, 395);
std::string str = id(alexa_shopping_list).state;
// Display position and line height
int xPos = 20, yPos = 430, lineHeight = 20;
// Split into lines where comma is delimiter
std::size_t curPos = 0, commaPos = 0;
while ((commaPos = str.find(',', curPos)) != std::string::npos) {
it.print(xPos, yPos, id(font_small_bold), str.substr(curPos, commaPos - curPos).c_str());
yPos += lineHeight;
curPos = commaPos + 1;
}
// FOOTER
//Divider draw a line from [x=0,y=0] to [x=50,y=50]
it.line(0, 665, 480, 665);
// Show date and time of last update
it.strftime(230, 700, id(font_small_bold), TextAlign::BASELINE_CENTER, "Updated: %Y-%m-%d %H:%M", id(ntp).now());
};
captive_portal: