Skip to content

Instantly share code, notes, and snippets.

@greghesp
Created November 13, 2024 22:01
Show Gist options
  • Save greghesp/c77dd40fb8644297012882c263a38370 to your computer and use it in GitHub Desktop.
Save greghesp/c77dd40fb8644297012882c263a38370 to your computer and use it in GitHub Desktop.
substitutions:
room_name: 'Living Room'
device_name: 'living-room-esp32'
friendly_name: 'Living Room ESP32'
description: 'Living Room Touch LCD with MQTT Room, Glucose Levels and Fireplace Temperature'
static_ip: 192.168.1.25
esp32:
board: esp32dev
framework:
type: arduino
esphome:
name: $device_name
friendly_name: $friendly_name
comment: $description
# logger:
# level: DEBUG
# Enable Home Assistant API
api:
ota:
- platform: esphome
# captive_portal:
mqtt:
broker: !secret mqtt_broker
username: !secret mqtt_username
password: !secret mqtt_password
discovery: false # Only if you use the HA API usually
id: mqtt_client
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: $static_ip
gateway: 192.168.1.1
subnet: 255.255.255.0
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: $device_name
time:
- platform: homeassistant
id: esptime
globals:
- id: is_glucose_hidden
type: bool
restore_value: no
initial_value: "false"
- id: last_update_timestamp
type: uint32_t
- id: last_update_seconds
type: uint32_t
- id: room_topic
type: std::string
initial_value: '"esphome-presence/${room_name}"'
sensor:
- platform: homeassistant
id: glucose_level
entity_id: sensor.dexcom_glucose_value
on_value:
- lambda: |-
id(last_update_timestamp) = millis();
- platform: homeassistant
id: linnea_temperature
entity_id: climate.linnea_heater
attribute: temperature
- platform: homeassistant
id: linnea_current_temperature
entity_id: climate.linnea_heater
attribute: current_temperature
text_sensor:
- platform: homeassistant
id: glucose_trend
entity_id: sensor.dexcom_glucose_trend
- platform: homeassistant
id: linnea
entity_id: climate.linnea_heater
- platform: homeassistant
id: glucose_range
entity_id: input_select.diabetes_glucose_threshold
color:
- id: my_red
red: 100%
green: 0%
blue: 0%
- id: my_orange
red: 100%
green: 50%
blue: 0%
- id: my_green
hex: 42BF73
- id: my_grey
hex: A0A0A0
- id: my_white
hex: FFFFFF
- id: my_blue
hex: 2977D3
font:
- file:
type: gfonts
family: Roboto+Slab
weight: 500
id: font_value
size: 48
- file:
type: gfonts
family: Roboto+Slab
weight: 500
id: temp_symbol
size: 18
- file:
type: gfonts
family: Roboto
weight: 300
id: font_mmol
size: 16
- file:
type: gfonts
family: Roboto
weight: 300
id: font_12
size: 12
- file: "gfonts://Roboto"
id: font_range
size: 16
image:
- file: mdi:fire
id: flame
resize: 70x70
- file: mdi:arrow-collapse-up
id: rising_quickly
resize: 30x30
- file: mdi:arrow-up
id: rising
resize: 30x30
- file: mdi:arrow-top-right
id: rising_slightly
resize: 30x30
- file: mdi:arrow-right
id: steady
resize: 30x30
- file: mdi:arrow-bottom-right
id: falling_slightly
resize: 30x30
- file: mdi:arrow-down
id: falling
resize: 30x30
- file: mdi:arrow-collapse-down
id: falling_quickly
resize: 30x30
- file: mdi:help
id: unknown
resize: 30x30
- file: mdi:arrow-up
id: up
resize: 20x20
- file: mdi:arrow-down
id: down
resize: 20x20
- file: mdi:eye
id: eye
resize: 20x20
- file: mdi:eye-off
id: eye_off
resize: 20x20
spi:
- id: lcd
clk_pin: GPIO14
mosi_pin: GPIO13
miso_pin: GPIO12
- id: touch
clk_pin: GPIO25
mosi_pin: GPIO32
miso_pin: GPIO39
i2c:
sda: GPIO27
scl: GPIO22
scan: true
id: bus_a
frequency: 400kHz
output:
- platform: ledc
pin: GPIO21
id: former_led_pin
- platform: ledc
id: output_red
pin: GPIO4
inverted: true
- platform: ledc
id: output_green
pin: GPIO16
inverted: true
- platform: ledc
id: output_blue
pin: GPIO17
inverted: true
display:
- platform: ili9xxx
invert_colors: false
model: ILI9341
spi_id: lcd
cs_pin: 15
dc_pin: 2
id: my_display
pages:
- id: glucose_fire
lambda: |-
int hs = it.get_width() / 2; // Horizontal Spacing = text data horizontal center point
int hq = it.get_width() / 4; // text data horizontal center for two vertical lines
int vs = it.get_height() / 8; // Vertical Center = text data vertical center point = how many lines
int hv = it.get_height() / 2;
int padding = 2;
int centre_gap = padding / 2;
// ***** Glucose Information ***** //
it.line(padding, padding, it.get_width() - padding, padding, id(my_green)); // Top Box Top Line
it.line(padding, padding, padding, hv - centre_gap, id(my_green)); // Top Box Left Line
it.line(it.get_width() - padding, padding, it.get_width() - padding, hv - centre_gap, id(my_green)); // Top Box Right Line
it.line(padding, hv - centre_gap, it.get_width() - padding, hv - centre_gap, id(my_green)); // Top Box Bottom Line
it.printf(padding + 5, padding + 5, id(font_12), "Last Updated: %i minutes ago", id(last_update_seconds)/60); // Last updated
it.image(it.get_width() - padding - 25 , padding + 5, id(eye_off), id(my_grey));
// Trend Circle Background
if (id(glucose_range).state == "Urgent High" || id(glucose_range).state == "Urgent Low") {
it.filled_circle(80, 75, 24, my_red);
} else if (id(glucose_range).state == "High" || id(glucose_range).state == "Low") {
it.filled_circle(80, 75, 24, my_orange);
} else {
it.filled_circle(80, 75, 24, my_green);
}
// Arrow Logic
if (id(glucose_trend).state == "rising_quickly") {
it.image(65, 60, id(rising_quickly), id(my_white));
} else if (id(glucose_trend).state == "rising") {
it.image(66, 60, id(rising), id(my_white));
} else if (id(glucose_trend).state == "rising_slightly") {
it.image(66, 60, id(rising_slightly), id(my_white));
} else if (id(glucose_trend).state == "steady") {
it.image(66, 60, id(steady), id(my_white));
} else if (id(glucose_trend).state == "falling_slightly") {
it.image(65, 60, id(falling_slightly), id(my_white));
} else if (id(glucose_trend).state == "falling") {
it.image(65, 60, id(falling), id(my_white));
} else if (id(glucose_trend).state == "falling_quickly") {
it.image(65, 60, id(falling_quickly), id(my_white));
} else {
it.image(65, 60, id(unknown), id(my_white));
}
it.printf(117, 39, id(font_value), "%.1f", id(glucose_level).state); // Glucose Value
it.print(117, 90, id(font_mmol), "mmol/L"); // mmol/L line
it.printf(hs, hv - centre_gap - 30, id(font_range), TextAlign::CENTER, "Glucose %s", id(glucose_trend).state.c_str()); // Glucose Trend String
// ***** Fireplace Information ***** //
it.line(padding, hv + centre_gap, it.get_width() - padding, hv + centre_gap, id(my_grey)); // Bottom Box Top Line
it.line(padding, hv + centre_gap, padding, it.get_height() - padding, id(my_grey)); // Bottom Box Left Line
it.line(it.get_width() - padding, hv + centre_gap, it.get_width() - padding, it.get_height() - padding, id(my_grey)); // Bottom Box Right Line
it.line(padding, it.get_height() - padding, it.get_width() - padding, it.get_height() - padding, id(my_grey)); // Top Box Bottom Line
if(id(linnea).state == "off") {
it.image(9, 205, id(flame), id(my_grey));
} else {
it.image(9, 205, id(flame), id(my_orange));
}
it.printf(85, 195, id(font_value), "%.0f", id(linnea_temperature).state); // Fireplace Temp Setting
it.print(135, 205, id(temp_symbol), "°C"); // Fireplace Temp Setting
it.printf(85, 250, id(font_mmol), "Now %.0f°C", id(linnea_current_temperature).state); // Current temp
it.filled_circle(200, 200, 20, my_red); // Up Circle Background
it.image(190, 190, id(up), id(my_white));
it.filled_circle(200, 270, 20, my_blue); // Down Circle Background
it.image(190, 262, id(down), id(my_white));
touchscreen:
platform: xpt2046
id: my_touchscreen
spi_id: touch
cs_pin: 33
interrupt_pin: 36
update_interval: 50ms
threshold: 400
calibration:
x_min: 3860
x_max: 280
y_min: 340
y_max: 3860
binary_sensor:
- platform: touchscreen
name: glucose_hide
id: glucose_hide
x_min: 200
x_max: 240
y_min: 0
y_max: 30
on_press:
- lambda: |-
if (id(is_glucose_hidden)) {
id(is_glucose_hidden) = false;
} else {
id(is_glucose_hidden) = true;
}
- platform: touchscreen
name: linnea_toggle
id: linnea_toggle
x_min: 5
x_max: 80
y_min: 200
y_max: 280
on_press:
- if:
condition:
text_sensor.state:
id: linnea
state: 'off'
then:
- homeassistant.service:
service: climate.turn_on
data:
entity_id: climate.linnea_heater
else:
- homeassistant.service:
service: climate.turn_off
data:
entity_id: climate.linnea_heater
- platform: touchscreen
name: linnea_up
id: linnea_up
x_min: 180
x_max: 220
y_min: 180
y_max: 220
on_press:
- homeassistant.service:
service: climate.set_temperature
data:
entity_id: climate.linnea_heater
data_template:
temperature: '{{ my_variable }}'
variables:
my_variable: |-
return id(linnea_temperature).state + 1;
- platform: touchscreen
name: linnea_down
id: linnea_down
x_min: 180
x_max: 220
y_min: 250
y_max: 290
on_press:
- homeassistant.service:
service: climate.set_temperature
data:
entity_id: climate.linnea_heater
data_template:
temperature: '{{ my_variable }}'
variables:
my_variable: |-
return id(linnea_temperature).state - 1;
light:
- platform: monochromatic
output: former_led_pin
name: "Display Backlight"
id: back_light
restore_mode: ALWAYS_ON
- platform: rgb
name: LED
red: output_red
id: led
green: output_green
blue: output_blue
restore_mode: ALWAYS_OFF
bluetooth_proxy:
active: true
esp32_ble_tracker:
on_ble_advertise:
- then:
- lambda: |-
if (x.get_ibeacon().has_value()) {
std::string uuid;
esp_bt_uuid_t raw_uuid = x.get_ibeacon().value().get_uuid().get_uuid();
char sbuf[64];
char *bpos = sbuf;
uuid = x.get_ibeacon().value().get_uuid().to_string();
std::transform(uuid.begin(), uuid.end(), uuid.begin(), [](unsigned char c){ return std::tolower(c); });
char mbuf[32] = {0};
sprintf(mbuf, "-%hu-%hu", x.get_ibeacon().value().get_major(), x.get_ibeacon().value().get_minor());
uuid.append(mbuf);
int8_t tx_power = -69;
float dist = pow(10, (float)(tx_power - x.get_rssi()) / (10 * 2));
ESP_LOGD("ble_adv", "Sending MQTT room update for '%s' (%s): %.03fm (%d rssi)",
x.get_name().c_str(), uuid.c_str(), dist, x.get_rssi());
id(mqtt_client).publish_json(id(room_topic), [=](JsonObject root) {
root["id"] = uuid;
root["name"] = x.get_name();
root["distance"] = dist;
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment