Hi,
I am trying to make a room occupation picture frame with an ePaper, that shows the occupation of an Office calendar for this room and has a physical button to book the next free slot. Later on I am planning to put the ESP in deepsleep, but this is going to be another story. At the moment I am trying to understand how I can “see” in HomeAssistant that the ESP started, do some calculations in HomeAssistant the let the ESP refresh its screen.
My basic idea, to keep the ESP “as dumb as possible”, and do all calculations of free slots, calendar data, … in HomeAssistant, which has the nice side effekt, that I can have a user interface in HomeAssistant as well.
What happens during boot:
If the ESP is restarted by “power on” it prints “Warte auf Daten” which is the default behaviour and “ok in general”, but it should than call the
esphome.${rmgr_device_name}.booted
and
event: esphome.${rmgr_device_name}_refresh_clicked
I use here
api:
on_client_connected:
then:
- logger.log: "Connected to HomeAssistant."
- homeassistant.event:
event: esphome.${rmgr_device_name}.booted
- logger.log: "simulte refresh to refresh the screen on first connect"
- homeassistant.event:
event: esphome.${rmgr_device_name}_refresh_clicked
I see the logger.log events on the ESP console, but in HomeAssistant I cannot see the event fired in the developer tools if I subscribe to
esphome.framery1_refresh_clicked
e.g. . If I run the esphome.framery1_refresh_clicked
manually in the developer tools on the HomeAssistant everything works as expected. (Screen on ePaper is updated, and the subscription show data.)
Any ideas? I am totally stuck.
Here the full code from the ESPHome. If HA yaml code is needed, just tell me. (In HA I get the data from the calendar, calculate a free slot and give all data to the epaper ESP, which works, “only” the firsst start does not work, which makes it impossible to run in deep sleep and wake up, which is what I tested yesterday. So this behaviour above, is what I nailed it down to.
# Calendar / Free Busy DASHBOARD
# For Home Assistant and ESPHome
# Inspired by WEATHERMAN by Madelena Mak 2022 - https://mmak.es
#
#######################
# VARIABLES passed into the system:
# - rmgr_device_name: the common prefix for sensors etc (still a bad name)
#
# ESPHome for battery:
# https://ncrmnt.org/2021/12/06/optimizing-esp8266-esphome-for-battery-power-and-making-an-ice-bath-thermometer-as-well/
# Core Configuration Section: https://esphome.io/components/esphome.html
esphome:
# https://esphome.io/components/esphome.html#changing-esphome-node-name
name: "${rmgr_device_name}"
# name is defined in specific instance config (e.g. framery1.yaml etc)
# https://esphome.io/components/esphome.html#on-boot
# on_boot is an *Automation* (https://esphome.io/automations/#automation)
on_boot: # == "Trigger"
priority: 200.0
then: # == "Actions"
- component.update: epaper_screen_print
- logger.log: "Forcing API reconnect after boot..."
- delay: 5s
# this triggers a reload in Home Assistant (which pushes the data down to the device)
# - homeassistant.event:
# event: "esphome.${rmgr_device_name}.booted"
# https://esphome.io/components/esp32.html
esp32:
# "if unsure choose a generic board from Espressif such as esp32dev."
board: esp32dev
framework:
type: arduino
# Enable logging
# https://esphome.io/components/logger.html
logger:
# level: NONE
# level: ERROR
# level: WARN
# level: INFO
level: DEBUG
# level: VERBOSE
# level: VERY_VERBOSE
# Global variables for detecting if the display needs to be refreshed. (Thanks @paviro!)
# https://esphome.io/components/globals.html
globals:
- id: initial_data_received
type: bool
restore_value: no
initial_value: 'false'
- id: headline_text
type: std::string
restore_value: no
- id: subheadline_text
type: std::string
restore_value: no
- id: timeslots
type: std::vector<std::string>
restore_value: no
- id: is_occupied
type: std::vector<bool>
restore_value: no
- id: rmgr_booking_slot_length_minutes
type: std::string
restore_value: no
- id: last_display_refresh
type: std::string
restore_value: no
# Enable Home Assistant API
# https://esphome.io/components/api.html
api:
on_client_connected:
then:
- logger.log: "Connected to HomeAssistant."
- homeassistant.event:
event: esphome.${rmgr_device_name}.booted
- logger.log: "simulte refresh to refresh the screen on first connect"
- homeassistant.event:
event: esphome.${rmgr_device_name}_refresh_clicked
services:
- service: "draw_schedule"
variables:
param_headline_text: string
param_subheadline_text: string
# !!! you need the same number of entries in param_timeslots and param_is_occupied
param_timeslots: string[]
param_is_occupied: bool[]
param_rmgr_booking_slot_length_minutes: string
param_last_display_refresh: string
then:
- lambda: 'id(initial_data_received) = true;'
- lambda: 'id(headline_text) = param_headline_text;'
- lambda: 'id(subheadline_text) = param_subheadline_text;'
- lambda: 'id(timeslots) = param_timeslots;'
- lambda: 'id(is_occupied) = param_is_occupied;'
- lambda: 'id(rmgr_booking_slot_length_minutes) = param_rmgr_booking_slot_length_minutes;'
- lambda: 'id(last_display_refresh) = param_last_display_refresh;'
- logger.log: ".-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. calfreebusy_draw_schedule Data"
- logger.log:
format: "param_headline_text has value >%s<"
args: [ 'id(headline_text).c_str()' ]
- logger.log:
format: "param_subheadline_text has value >%s<"
args: [ 'id(subheadline_text).c_str()' ]
- logger.log:
format: "param_timeslots has value >%i<"
args: [ 'id(timeslots)' ]
- component.update: epaper_screen_print
# https://esphome.io/components/ota/esphome
ota:
- platform: esphome
# ota turns safe_mode on, which we do not want.
safe_mode:
boot_is_good_after: 30s
# Wifi information
wifi:
networks:
# channel: 11
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
# ausgeshcaltet, da es
# ap:
# ssid: !secret wifi_ssid
# password: !secret wifi_password
# Include custom fonts
font:
- file: 'fonts/GothamRnd-Book.ttf'
id: font_small_book
size: 18
glyphs: ['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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.', '%', 'ä', 'ö', 'ü', 'ß', '(', ')']
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_large_bold
size: 108
glyphs: ['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', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', '%']
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_title
size: 54
glyphs: ['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', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', 'u', 'p', 'd', 't', '%']
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_medium_bold
size: 30
glyphs: ['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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.', '%', 'ä', 'ö', 'ü', 'ß', '(', ')']
- file: 'fonts/GothamRnd-Bold.ttf'
id: font_small_bold
size: 18
glyphs: ['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', 'Ä', 'Ö', 'Ü', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '-', ' ', '°', '.','m', '%']
# Include Material Design Icons font
# Thanks to https://community.home-assistant.io/t/display-materialdesign-icons-on-esphome-attached-to-screen/199790/16
- file: 'fonts/materialdesignicons-webfont.ttf'
id: font_mdi_medlarge
size: 18
glyphs:
- "\U000F05A9" # mdi-wifi
- "\U000F0928" # mdi-wifi-strength-4
- "\U000F0925" # mdi-wifi-strength-3
- "\U000F0922" # mdi-wifi-strength-2
- "\U000F091F" # mdi-wifi-strength-1
- "\U000F092B" # mdi-wifi-strength-alert-outline
# Check if epaper should be refreshed
sensor:
- platform: wifi_signal
name: "${rmgr_device_name} - WiFi Signal Strength"
id: "${rmgr_device_name}_wifisignal"
unit_of_measurement: "dBm"
entity_category: "diagnostic"
update_interval: 600s
binary_sensor:
# refresh durch HomeAssistant
- platform: homeassistant
entity_id: input_boolean.${rmgr_device_name}_refresh_clicked
id: update_epaper_display
# book durch Button am ESP
- platform: gpio
name: "${rmgr_device_name}_book_now"
pin:
number: GPIO16
inverted: true
mode:
input: true
pullup: true
filters:
- delayed_on: 10ms
on_multi_click:
- timing:
- ON for at most 1s
- OFF for at most 1s
- ON for at most 1s
- OFF for at least 0.2s
then:
- logger.log: "Double Clicked"
- homeassistant.event:
event: esphome.${rmgr_device_name}_booknow_doubleclicked
- timing: # INFO: this is currently not used functionally.
- ON for 1s to 2s
- OFF for at least 0.5s
then:
- logger.log: "Single Long Clicked"
- homeassistant.event:
event: esphome.${rmgr_device_name}_booknow_longclicked
- timing:
- ON for at most 1s
- OFF for at least 0.5s
then:
- logger.log: "Single Short Clicked"
- homeassistant.event:
event: esphome.${rmgr_device_name}_booknow_clicked
## refresh durch Button am ESP
# Commented out, as this iwll later on be GPIO12 to wake the ESP up and
# during wakeup the refresh will happen.
# - platform: gpio
# name: "${rmgr_device_name}_refresh"
# pin:
# number: GPIO17
# inverted: true
# mode:
# input: true
# pullup: true
# filters:
# - delayed_on: 10ms
# on_multi_click:
# - timing:
# - ON for at most 1s
# - OFF for at most 1s
# - ON for at most 1s
# - OFF for at least 0.2s
# then:
# - logger.log: "Double Clicked"
# - homeassistant.event:
# event: esphome.${rmgr_device_name}_refresh_doubleclicked
# - timing: # INFO: this is currently not used functionally.
# - ON for 1s to 2s
# - OFF for at least 0.5s
# then:
# - logger.log: "Single Long Clicked"
# - homeassistant.event:
# event: esphome.${rmgr_device_name}_refresh_longclicked
# - timing:
# - ON for at most 1s
# - OFF for at least 0.5s
# then:
# - logger.log: "Single Short Clicked"
# - homeassistant.event:
# event: esphome.${rmgr_device_name}_refresh_clicked
# Pins for Waveshare ePaper ESP Board
spi:
clk_pin: GPIO13 # SCLK P13 SPI CLK pin, clock signal input
mosi_pin: GPIO14 # DIN P14 SPI MOSI pin, data input
# Now render everything on the ePaper screen.
display:
- platform: waveshare_epaper
# models from https://esphome.io/components/display/waveshare_epaper
# model: 7.50inV2 # s/w board evtl. älterer Treiber ???
# model: 7.50in-nV2 # s/w board
model: 7.50in-bV3 # r/s/w board
id: epaper_screen_print
cs_pin: GPIO15 # CS P15 Chip selection, low active
dc_pin: GPIO27 # DC P27 Data/command, low for commands, high for data
busy_pin:
number: GPIO25 # BUSY P25 Busy status output pin (means busy)
inverted: true
reset_pin: GPIO26 # RST P26 Reset, low active
reset_duration: 2ms
update_interval: never
rotation: 90°
lambda: |-
int offsetY = 40;
int offsetX = 0;
//# Print loading screen before data is received.
if (id(initial_data_received) == false) {
it.printf(240, 390, id(font_small_bold), TextAlign::TOP_CENTER, "WARTE AUF DATEN");
} else {
// headline
int lineHeight = 35;
it.printf(50, offsetY, id(font_medium_bold), TextAlign::TOP_LEFT, "%s", id(headline_text).c_str());
offsetY += lineHeight;
// subheadline
lineHeight = 22;
it.printf(150, offsetY, id(font_small_book), TextAlign::TOP_LEFT, "%s", id(subheadline_text).c_str());
offsetY += lineHeight;
lineHeight = 22;
for (size_t i = 0; i < id(timeslots).size(); ++i) {
offsetY += lineHeight;
it.printf(50, offsetY, id(font_small_book), TextAlign::TOP_LEFT, "%s", id(timeslots)[i].c_str());
if (id(is_occupied)[i]) {
it.filled_rectangle(120, offsetY, 100, lineHeight);
}
}
}
//# decide for the WiFi-signal Symbol
//# is printed printed below
std::string wifiSymbol = "";
offsetX = it.get_width() - 50;
offsetY = it.get_height() - 150;
if(id(${rmgr_device_name}_wifisignal).has_state ()) {
if (id(${rmgr_device_name}_wifisignal).state >= -50) {
// Excellent
wifiSymbol = "\U000F0928";
} else if (id(${rmgr_device_name}_wifisignal).state >= -60) {
//Good
wifiSymbol = "\U000F0925";
} else if (id(${rmgr_device_name}_wifisignal).state >= -67) {
//Fair
wifiSymbol = "\U000F0922";
} else if (id(${rmgr_device_name}_wifisignal).state >= -70) {
//Weak
wifiSymbol = "\U000F091F";
} else {
//Unlikely working signal
wifiSymbol = "\U000F092B";
}
}
// Print booking slot length + Last refresh + Wifi
// we write from bottom to top
offsetX = it.get_width() - 50;
offsetY = it.get_height() - 130;
int lineHeight = 22;
// Print DBI and Wifi Signal
it.printf(offsetX - 25, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%.0f dB", id(${rmgr_device_name}_wifisignal).state);
it.printf(offsetX, offsetY, id(font_mdi_medlarge), TextAlign::TOP_RIGHT, "%s", wifiSymbol.c_str());
//
it.printf(offsetX - 150, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%s", id(rmgr_booking_slot_length_minutes).c_str());
offsetY -= lineHeight;
it.printf(offsetX, offsetY, id(font_small_book), TextAlign::TOP_RIGHT, "%s", id(last_display_refresh).c_str());
captive_portal: