-
-
Notifications
You must be signed in to change notification settings - Fork 33
Open
Description
I have been trying to work out what is going on with the deep sleep and not getting updated stats. I think it is because the api feeds the info from home assistant and because HA cant see the device in deep sleep it backs off sending through the updated values.
I think to enable deep sleep mode the device needs to be changed from API to mqtt?
Also I don't think the code for silent hours runs when in deep sleep mode?
# ============================================================================
# ESPHome YAML - Generated by ESPHome Designer
# ============================================================================
# TARGET DEVICE: Seeed Studio Trmnl DIY Kit (ESP32-S3)
# - Display Platform: waveshare_epaper
# - PSRAM: Yes
# - Touchscreen: No
# - Framework: esp-idf (Recommended)
# ============================================================================
#
# SETUP INSTRUCTIONS:
#
# STEP 1: Copy the Material Design Icons font file
# - From this repo: resources/fonts/materialdesignicons-webfont.ttf
# - To ESPHome: /config/esphome/fonts/materialdesignicons-webfont.ttf
#
# STEP 2: Create a new device in ESPHome
# - Click "New Device"
# - Select: ESP32-S3 (or appropriate for your board)
# - Framework: ESP-IDF (Essential for S3 stability)
#
# STEP 3: PASTE this snippet into your device YAML
# - Paste this snippet at the end of your configuration.
# - System sections (esphome, esp32, psram) are auto-commented
# to avoid conflicts with your existing base setup.
#
# CAPTIVE PORTAL:
# - If WiFi connection fails, the device will create a hotspot.
# - Search for its name in your WiFi settings.
# - Connect and go to http://192.168.4.1 to configure WiFi.
#
# TIP: For reTerminal / S3 devices, if you cannot see logs via USB,
# add this to your base 'logger:' section:
# hardware_uart: USB_CDC
#
# ============================================================================
# ====================================
# Device Settings
# ====================================
# Orientation: landscape
# Dark Mode: disabled
# Refresh Interval: 600
# Power Strategy: Ultra Eco (Deep Sleep)
# Deep Sleep Interval: 600
# ====================================
# esphome:
# name: your-device-name
# comment: 'Snippet generated by ESPHome Designer'
# on_boot:
# priority: 300
# then:
# - output.turn_on: bsp_battery_enable
# - delay: 2s
# - script.execute: deep_sleep_cycle
#
# esp32:
# board: esp32-s3-devkitc-1
# framework:
# type: esp-idf
# sdkconfig_options:
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: y
# CONFIG_ESP32S3_DATA_CACHE_64KB: y
#
# logger:
# hardware_uart: USB_CDC # Enable for USB debugging on S3
# level: DEBUG
#
# api:
# ota:
# wifi:
# # ... your wifi config here
deep_sleep:
id: deep_sleep_control
run_duration: 30s # Stay awake 30s on boot for OTA
sleep_duration: 600s
globals:
- id: display_page
type: int
restore_value: true
initial_value: '0'
- id: page_refresh_default_s
type: int
restore_value: true
initial_value: '600'
- id: page_refresh_current_s
type: int
restore_value: false
initial_value: '60'
- id: last_page_switch_time
type: uint32_t
restore_value: false
initial_value: '0'
psram:
mode: octal
speed: 80MHz
http_request:
verify_ssl: false
timeout: 20s
buffer_size_rx: 4096
i2c:
- sda: GPIO17
scl: GPIO18
scan: true
id: bus_a
spi:
- id: spi_bus
clk_pin: GPIO7
mosi_pin: GPIO9
output:
- platform: gpio
pin: GPIO6
id: bsp_battery_enable
time:
- platform: homeassistant
id: ha_time
sensor:
- platform: adc
pin: GPIO1
name: "Battery Voltage"
unit_of_measurement: "V"
device_class: voltage
state_class: measurement
id: battery_voltage
update_interval: 60s
attenuation: 12db
filters:
- multiply: 2
- platform: template
name: "Battery Level"
id: battery_level
unit_of_measurement: "%"
icon: "mdi:battery"
device_class: battery
state_class: measurement
lambda: 'return id(battery_voltage).state;'
update_interval: 60s
filters:
- calibrate_linear:
- 4.15 -> 100
- 3.96 -> 90
- 3.91 -> 80
- 3.85 -> 70
- 3.8 -> 60
- 3.75 -> 50
- 3.68 -> 40
- 3.58 -> 30
- 3.49 -> 20
- 3.41 -> 10
- 3.3 -> 5
- 3.27 -> 0
- clamp:
min_value: 0
max_value: 100
- platform: homeassistant
id: weather_high_day0
entity_id: sensor.weather_forecast_day_0_high
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_low_day0
entity_id: sensor.weather_forecast_day_0_low
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_high_day1
entity_id: sensor.weather_forecast_day_1_high
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_low_day1
entity_id: sensor.weather_forecast_day_1_low
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_high_day2
entity_id: sensor.weather_forecast_day_2_high
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_low_day2
entity_id: sensor.weather_forecast_day_2_low
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_high_day3
entity_id: sensor.weather_forecast_day_3_high
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_low_day3
entity_id: sensor.weather_forecast_day_3_low
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_high_day4
entity_id: sensor.weather_forecast_day_4_high
unit_of_measurement: '°C'
internal: true
- platform: homeassistant
id: weather_low_day4
entity_id: sensor.weather_forecast_day_4_low
unit_of_measurement: '°C'
internal: true
- platform: wifi_signal
id: wifi_signal_dbm
internal: true
- platform: homeassistant
id: sensor_greenhouse_temperature
entity_id: sensor.greenhouse_temperature
internal: true
- platform: homeassistant
id: sensor_outside_temperature
entity_id: sensor.outside_temperature
internal: true
- platform: homeassistant
id: sensor_kitchen_temp_temperature
entity_id: sensor.kitchen_temp_temperature
internal: true
- platform: homeassistant
id: sensor_lounge_temp_temperature
entity_id: sensor.lounge_temp_temperature
internal: true
- platform: homeassistant
id: sensor_0x00158d00044a3bca_temperature
entity_id: sensor.0x00158d00044a3bca_temperature
internal: true
- platform: homeassistant
id: sensor_bathroom_temperature
entity_id: sensor.bathroom_temperature
internal: true
- platform: homeassistant
id: sensor_basement_sensor_temperature
entity_id: sensor.basement_sensor_temperature
internal: true
- platform: homeassistant
id: sensor_greenhouse_humidity
entity_id: sensor.greenhouse_humidity
internal: true
- platform: homeassistant
id: sensor_outside_humidity
entity_id: sensor.outside_humidity
internal: true
- platform: homeassistant
id: sensor_kitchen_temp_humidity
entity_id: sensor.kitchen_temp_humidity
internal: true
- platform: homeassistant
id: sensor_lounge_temp_humidity
entity_id: sensor.lounge_temp_humidity
internal: true
- platform: homeassistant
id: sensor_0x00158d00044a3bca_humidity
entity_id: sensor.0x00158d00044a3bca_humidity
internal: true
- platform: homeassistant
id: sensor_bathroom_humidity
entity_id: sensor.bathroom_humidity
internal: true
- platform: homeassistant
id: sensor_basement_sensor_humidity
entity_id: sensor.basement_sensor_humidity
internal: true
- platform: homeassistant
id: sensor_tautulli_watching
entity_id: sensor.tautulli_watching
internal: true
- platform: homeassistant
id: sensor_tautulli_direct_streams
entity_id: sensor.tautulli_direct_streams
internal: true
- platform: homeassistant
id: sensor_tautulli_direct_plays
entity_id: sensor.tautulli_direct_plays
internal: true
- platform: homeassistant
id: sensor_tautulli_transcodes
entity_id: sensor.tautulli_transcodes
internal: true
text_sensor:
# Weather Forecast Condition Sensors
- platform: homeassistant
id: weather_cond_day0
entity_id: sensor.weather_forecast_day_0_condition
internal: true
- platform: homeassistant
id: weather_cond_day1
entity_id: sensor.weather_forecast_day_1_condition
internal: true
- platform: homeassistant
id: weather_cond_day2
entity_id: sensor.weather_forecast_day_2_condition
internal: true
- platform: homeassistant
id: weather_cond_day3
entity_id: sensor.weather_forecast_day_3_condition
internal: true
- platform: homeassistant
id: weather_cond_day4
entity_id: sensor.weather_forecast_day_4_condition
internal: true
# ============================================================================
# HOME ASSISTANT TEMPLATE SENSORS
# Add these template sensors to your Home Assistant configuration.yaml:
# ============================================================================
#
# template:
# - trigger:
# - trigger: state
# entity_id: weather.forecast_home
# - trigger: time_pattern
# hours: '/1'
# action:
# - action: weather.get_forecasts
# target:
# entity_id: weather.forecast_home
# data:
# type: daily
# response_variable: forecast_data
# sensor:
# - name: 'Weather Forecast Day 0 High'
# unique_id: weather_forecast_day_0_high
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[0].temperature | default("N/A") }}'
# - name: 'Weather Forecast Day 0 Low'
# unique_id: weather_forecast_day_0_low
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[0].templow | default("N/A") }}'
# - name: 'Weather Forecast Day 0 Condition'
# unique_id: weather_forecast_day_0_condition
# state: '{{ forecast_data["weather.forecast_home"].forecast[0].condition | default("cloudy") }}'
# - name: 'Weather Forecast Day 1 High'
# unique_id: weather_forecast_day_1_high
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[1].temperature | default("N/A") }}'
# - name: 'Weather Forecast Day 1 Low'
# unique_id: weather_forecast_day_1_low
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[1].templow | default("N/A") }}'
# - name: 'Weather Forecast Day 1 Condition'
# unique_id: weather_forecast_day_1_condition
# state: '{{ forecast_data["weather.forecast_home"].forecast[1].condition | default("cloudy") }}'
# - name: 'Weather Forecast Day 2 High'
# unique_id: weather_forecast_day_2_high
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[2].temperature | default("N/A") }}'
# - name: 'Weather Forecast Day 2 Low'
# unique_id: weather_forecast_day_2_low
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[2].templow | default("N/A") }}'
# - name: 'Weather Forecast Day 2 Condition'
# unique_id: weather_forecast_day_2_condition
# state: '{{ forecast_data["weather.forecast_home"].forecast[2].condition | default("cloudy") }}'
# - name: 'Weather Forecast Day 3 High'
# unique_id: weather_forecast_day_3_high
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[3].temperature | default("N/A") }}'
# - name: 'Weather Forecast Day 3 Low'
# unique_id: weather_forecast_day_3_low
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[3].templow | default("N/A") }}'
# - name: 'Weather Forecast Day 3 Condition'
# unique_id: weather_forecast_day_3_condition
# state: '{{ forecast_data["weather.forecast_home"].forecast[3].condition | default("cloudy") }}'
# - name: 'Weather Forecast Day 4 High'
# unique_id: weather_forecast_day_4_high
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[4].temperature | default("N/A") }}'
# - name: 'Weather Forecast Day 4 Low'
# unique_id: weather_forecast_day_4_low
# unit_of_measurement: '°C'
# state: '{{ forecast_data["weather.forecast_home"].forecast[4].templow | default("N/A") }}'
# - name: 'Weather Forecast Day 4 Condition'
# unique_id: weather_forecast_day_4_condition
# state: '{{ forecast_data["weather.forecast_home"].forecast[4].condition | default("cloudy") }}'
#
# ============================================================================
binary_sensor:
- platform: gpio
pin:
number: GPIO2
mode: INPUT_PULLUP
inverted: true
name: "Left Button"
id: button_left
on_press:
then:
- script.execute:
id: change_page_to
target_page: !lambda 'return id(display_page) > 0 ? id(display_page) - 1 : 0;'
- platform: gpio
pin:
number: GPIO5
mode: INPUT_PULLUP
inverted: true
name: "Refresh Button"
id: button_refresh
on_press:
then:
- component.update: epaper_display
button:
- platform: template
name: "Next Page"
on_press:
then:
- script.execute:
id: change_page_to
target_page: !lambda 'return id(display_page) + 1;'
- platform: template
name: "Previous Page"
on_press:
then:
- script.execute:
id: change_page_to
target_page: !lambda 'return id(display_page) - 1;'
- platform: template
name: "Refresh Display"
on_press:
then:
- component.update: epaper_display
- platform: template
name: "Go to Page 1"
on_press:
then:
- script.execute:
id: change_page_to
target_page: 0
font:
- file:
type: gfonts
family: "Merriweather"
weight: 700
italic: false
id: font_merriweather_700_20
size: 20
glyphsets: [GF_Latin_Kernel]
ignore_missing_glyphs: true
- file:
type: gfonts
family: "Merriweather"
weight: 400
italic: false
id: font_merriweather_400_14
size: 14
glyphsets: [GF_Latin_Kernel]
ignore_missing_glyphs: true
- file:
type: gfonts
family: "Roboto"
weight: 400
italic: false
id: font_roboto_400_16
size: 16
glyphsets: [GF_Latin_Kernel]
ignore_missing_glyphs: true
- file:
type: gfonts
family: "Merriweather"
weight: 700
italic: false
id: font_merriweather_700_14
size: 14
glyphsets: [GF_Latin_Kernel]
ignore_missing_glyphs: true
- file:
type: gfonts
family: "Merriweather"
weight: 400
italic: false
id: font_merriweather_400_20
size: 20
glyphsets: [GF_Latin_Kernel]
ignore_missing_glyphs: true
- file:
type: gfonts
family: "Merriweather"
weight: 400
italic: false
id: font_merriweather_400_16
size: 16
glyphsets: [GF_Latin_Kernel]
ignore_missing_glyphs: true
- file: "fonts/materialdesignicons-webfont.ttf"
id: font_material_design_icons_400_32
size: 32
glyphs: ["\U000F0026", "\U000F0590", "\U000F0591", "\U000F0592", "\U000F0593", "\U000F0594", "\U000F0595", "\U000F0596", "\U000F0597", "\U000F0598", "\U000F0599", "\U000F059D", "\U000F059E", "\U000F067E", "\U000F067F"]
- file: "fonts/materialdesignicons-webfont.ttf"
id: font_material_design_icons_400_24
size: 24
glyphs: ["\U000F0079", "\U000F007B", "\U000F007E", "\U000F0082", "\U000F0083", "\U000F091F", "\U000F0922", "\U000F0925", "\U000F0928", "\U000F092B"]
- file: "fonts/materialdesignicons-webfont.ttf"
id: font_material_design_icons_400_40
size: 40
glyphs: ["\U000F050F"]
- file: "fonts/materialdesignicons-webfont.ttf"
id: font_material_design_icons_400_48
size: 48
glyphs: ["\U000F058E", "\U000F06BA"]
script:
- id: change_page_to
parameters:
target_page: int
then:
- lambda: |-
int pages_count = 1;
int target = target_page;
while (target < 0) target += pages_count;
target %= pages_count;
// Debounce: Ignore page changes within 3000ms of last change
// (adjusted for e-paper display update time)
uint32_t now = millis();
if (now - id(last_page_switch_time) < 3000) {
ESP_LOGD("display", "Page change ignored (debounce), last switch was %d ms ago", now - id(last_page_switch_time));
return;
}
if (id(display_page) != target) {
// Set debounce time BEFORE display update (update takes ~1.6s)
id(last_page_switch_time) = now;
id(display_page) = target;
id(epaper_display).update();
ESP_LOGI("display", "Switched to page %d", target);
// Restart refresh logic
if (id(manage_run_and_sleep).is_running()) id(manage_run_and_sleep).stop();
id(manage_run_and_sleep).execute();
}
- id: deep_sleep_cycle
then:
- logger.log: "Waiting for sync before Deep Sleep..."
- wait_until:
condition:
lambda: 'return id(ha_time).now().is_valid() && api_is_connected();'
timeout: 60s
- delay: 5s
- component.update: epaper_display
- delay: 5s # Ensure refresh starts before sleep
- logger.log: "Entering Deep Sleep now..."
- deep_sleep.enter: deep_sleep_control
- id: manage_run_and_sleep
mode: restart
then:
- logger.log: "Waiting for sync..."
- wait_until:
condition:
lambda: 'return id(ha_time).now().is_valid() && api_is_connected();'
timeout: 60s
- delay: 5s
- lambda: |-
int p = id(display_page);
int interval = id(page_refresh_default_s);
bool is_sleep_time = false;
auto time = id(ha_time).now();
if (time.is_valid()) {
int hour = time.hour;
int start = 0;
int end = 5;
if (start < end) {
if (hour >= start && hour < end) is_sleep_time = true;
} else {
if (hour >= start || hour < end) is_sleep_time = true;
}
}
if (!is_sleep_time) {
}
id(page_refresh_current_s) = interval;
- component.update: epaper_display
- delay: !lambda 'return id(page_refresh_current_s) * 1000;'
- script.execute: manage_run_and_sleep
display:
- platform: waveshare_epaper
id: epaper_display
cs_pin: GPIO44
dc_pin: GPIO10
reset_pin: GPIO38
busy_pin:
number: GPIO4
inverted: true
model: "7.50inv2p"
rotation: 0
update_interval: never
full_update_every: 30
lambda: |-
const auto COLOR_WHITE = Color(0, 0, 0); // Inverted for e-ink
const auto COLOR_BLACK = Color(255, 255, 255); // Inverted for e-ink
const auto COLOR_RED = Color(255, 0, 0);
const auto COLOR_GREEN = Color(0, 255, 0);
const auto COLOR_BLUE = Color(0, 0, 255);
const auto COLOR_YELLOW = Color(255, 255, 0);
const auto COLOR_ORANGE = Color(255, 165, 0);
auto color_off = COLOR_WHITE;
auto color_on = COLOR_BLACK;
// Helper to print text with word-wrap at widget boundary
auto print_wrapped_text = [&](int x, int y, int max_w, int line_h, esphome::font::Font *font, Color color, TextAlign align, const char* text) {
if (!text || max_w <= 0) return;
int cx = x;
int cy = y;
std::string line;
std::string word;
const char* p = text;
while (*p) {
// SANITIZATION: Treat newlines, carriage returns, and tabs as spaces for flow
bool is_space = (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t');
if (is_space) {
if (!word.empty()) {
int ww, wh, wbl, wx;
font->measure(word.c_str(), &ww, &wx, &wbl, &wh);
int lw = 0, lx;
if (!line.empty()) { font->measure(line.c_str(), &lw, &lx, &wbl, &wh); int sw, sx, sbl, sh; font->measure(" ", &sw, &sx, &sbl, &sh); lw += sw; }
if (lw + ww > max_w && !line.empty()) {
it.print(cx, cy, font, color, align, line.c_str());
cy += line_h;
line = word;
} else {
if (!line.empty()) line += " ";
line += word;
}
word.clear();
}
} else {
word += *p;
}
p++;
}
if (!word.empty()) {
int ww, wh, wbl, wx;
font->measure(word.c_str(), &ww, &wx, &wbl, &wh);
int lw = 0, lx;
if (!line.empty()) { font->measure(line.c_str(), &lw, &lx, &wbl, &wh); int sw, sx, sbl, sh; font->measure(" ", &sw, &sx, &sbl, &sh); lw += sw; }
if (lw + ww > max_w && !line.empty()) {
it.print(cx, cy, font, color, align, line.c_str());
cy += line_h;
line = word;
} else {
if (!line.empty()) line += " ";
line += word;
}
}
if (!line.empty()) {
it.print(cx, cy, font, color, align, line.c_str());
}
};
// Helper to apply a simple grey dither mask for e-paper (checkerboard)
auto apply_grey_dither_mask = [&](int x_start, int y_start, int w, int h) {
for (int y = y_start; y < y_start + h; y++) {
for (int x = x_start; x < x_start + w; x++) {
if ((x + y) % 2 == 0) it.draw_pixel_at(x, y, COLOR_WHITE);
else it.draw_pixel_at(x, y, COLOR_BLACK);
}
}
};
// Helper to apply grey dither to text (subtractive - erases every other black pixel)
auto apply_grey_dither_to_text = [&](int x_start, int y_start, int w, int h) {
for (int y = y_start; y < y_start + h; y++) {
for (int x = x_start; x < x_start + w; x++) {
if ((x + y) % 2 == 0) it.draw_pixel_at(x, y, COLOR_WHITE);
}
}
};
int currentPage = id(display_page);
if (currentPage == 0) {
// page:name "Overview"
// page:dark_mode "light"
// page:refresh_type "interval"
// page:refresh_time ""
// Clear screen for this page
it.fill(COLOR_WHITE);
color_off = COLOR_WHITE;
color_on = COLOR_BLACK;
// widget:line id:w_mlbx8rnxhm16x type:line x:100 y:0 w:3 h:483 stroke:3 color:theme_auto orientation:vertical
it.filled_rectangle(100, 0, 3, 483, color_on);
// widget:weather_forecast id:w_mlc06jo94k2vr type:weather_forecast x:0 y:0 w:88 h:480 weather_entity:"weather.forecast_home" layout:vertical show_high_low:true day_font_size:20 temp_font_size:14 icon_size:32 font_family:"Merriweather" color:theme_auto precision:0
{
static std::map<std::string, const char*> weather_icons = {
{"clear-night", "\U000F0594"}, {"cloudy", "\U000F0590"},
{"exceptional", "\U000F0026"}, {"fog", "\U000F0591"},
{"hail", "\U000F0592"}, {"lightning", "\U000F0593"},
{"lightning-rainy", "\U000F067E"}, {"partlycloudy", "\U000F0595"},
{"pouring", "\U000F0596"}, {"rainy", "\U000F0597"},
{"snowy", "\U000F0598"}, {"snowy-rainy", "\U000F067F"},
{"sunny", "\U000F0599"}, {"windy", "\U000F059D"},
{"windy-variant", "\U000F059E"}
};
auto get_icon = [&](const std::string& cond_val) -> const char* {
return weather_icons.count(cond_val) ? weather_icons[cond_val] : "\U000F0590";
};
auto get_day_name = [](int offset) -> std::string {
if (offset == 0) return "Today";
auto t = id(ha_time).now();
if (!t.is_valid()) return "---";
ESPTime future = ESPTime::from_epoch_local(t.timestamp + (offset * 86400));
char buf[8]; future.strftime(buf, sizeof(buf), "%a");
return std::string(buf);
};
for (int i = 0; i < 1; i++) {
it.rectangle(0 + i, 0 + i, 88 - 2 * i, 480 - 2 * i, COLOR_WHITE);
}
{
int dx = 0; int dy = 0 + 3;
it.printf(dx + 44, dy, id(font_merriweather_700_20), color_on, TextAlign::TOP_CENTER, "%s", get_day_name(0).c_str());
std::string cond_day = id(weather_cond_day0).state.c_str();
it.printf(dx + 44, dy + 24, id(font_material_design_icons_400_32), color_on, TextAlign::TOP_CENTER, "%s", get_icon(cond_day));
float high = id(weather_high_day0).state; float low = id(weather_low_day0).state;
char temp_buf[32];
if (std::isnan(high) && std::isnan(low)) {
sprintf(temp_buf, "--/--");
} else if (std::isnan(high)) {
sprintf(temp_buf, "--/%.*f°C", 0, low);
} else if (std::isnan(low)) {
sprintf(temp_buf, "%.*f°C/--", 0, high);
} else {
sprintf(temp_buf, "%.*f/%.*f°C", 0, high, 0, low);
}
it.printf(dx + 44, dy + 60, id(font_merriweather_400_14), color_on, TextAlign::TOP_CENTER, "%s", temp_buf);
}
{
int dx = 0; int dy = 96 + 3;
it.printf(dx + 44, dy, id(font_merriweather_700_20), color_on, TextAlign::TOP_CENTER, "%s", get_day_name(1).c_str());
std::string cond_day = id(weather_cond_day1).state.c_str();
it.printf(dx + 44, dy + 24, id(font_material_design_icons_400_32), color_on, TextAlign::TOP_CENTER, "%s", get_icon(cond_day));
float high = id(weather_high_day1).state; float low = id(weather_low_day1).state;
char temp_buf[32];
if (std::isnan(high) && std::isnan(low)) {
sprintf(temp_buf, "--/--");
} else if (std::isnan(high)) {
sprintf(temp_buf, "--/%.*f°C", 0, low);
} else if (std::isnan(low)) {
sprintf(temp_buf, "%.*f°C/--", 0, high);
} else {
sprintf(temp_buf, "%.*f/%.*f°C", 0, high, 0, low);
}
it.printf(dx + 44, dy + 60, id(font_merriweather_400_14), color_on, TextAlign::TOP_CENTER, "%s", temp_buf);
}
{
int dx = 0; int dy = 192 + 3;
it.printf(dx + 44, dy, id(font_merriweather_700_20), color_on, TextAlign::TOP_CENTER, "%s", get_day_name(2).c_str());
std::string cond_day = id(weather_cond_day2).state.c_str();
it.printf(dx + 44, dy + 24, id(font_material_design_icons_400_32), color_on, TextAlign::TOP_CENTER, "%s", get_icon(cond_day));
float high = id(weather_high_day2).state; float low = id(weather_low_day2).state;
char temp_buf[32];
if (std::isnan(high) && std::isnan(low)) {
sprintf(temp_buf, "--/--");
} else if (std::isnan(high)) {
sprintf(temp_buf, "--/%.*f°C", 0, low);
} else if (std::isnan(low)) {
sprintf(temp_buf, "%.*f°C/--", 0, high);
} else {
sprintf(temp_buf, "%.*f/%.*f°C", 0, high, 0, low);
}
it.printf(dx + 44, dy + 60, id(font_merriweather_400_14), color_on, TextAlign::TOP_CENTER, "%s", temp_buf);
}
{
int dx = 0; int dy = 288 + 3;
it.printf(dx + 44, dy, id(font_merriweather_700_20), color_on, TextAlign::TOP_CENTER, "%s", get_day_name(3).c_str());
std::string cond_day = id(weather_cond_day3).state.c_str();
it.printf(dx + 44, dy + 24, id(font_material_design_icons_400_32), color_on, TextAlign::TOP_CENTER, "%s", get_icon(cond_day));
float high = id(weather_high_day3).state; float low = id(weather_low_day3).state;
char temp_buf[32];
if (std::isnan(high) && std::isnan(low)) {
sprintf(temp_buf, "--/--");
} else if (std::isnan(high)) {
sprintf(temp_buf, "--/%.*f°C", 0, low);
} else if (std::isnan(low)) {
sprintf(temp_buf, "%.*f°C/--", 0, high);
} else {
sprintf(temp_buf, "%.*f/%.*f°C", 0, high, 0, low);
}
it.printf(dx + 44, dy + 60, id(font_merriweather_400_14), color_on, TextAlign::TOP_CENTER, "%s", temp_buf);
}
{
int dx = 0; int dy = 384 + 3;
it.printf(dx + 44, dy, id(font_merriweather_700_20), color_on, TextAlign::TOP_CENTER, "%s", get_day_name(4).c_str());
std::string cond_day = id(weather_cond_day4).state.c_str();
it.printf(dx + 44, dy + 24, id(font_material_design_icons_400_32), color_on, TextAlign::TOP_CENTER, "%s", get_icon(cond_day));
float high = id(weather_high_day4).state; float low = id(weather_low_day4).state;
char temp_buf[32];
if (std::isnan(high) && std::isnan(low)) {
sprintf(temp_buf, "--/--");
} else if (std::isnan(high)) {
sprintf(temp_buf, "--/%.*f°C", 0, low);
} else if (std::isnan(low)) {
sprintf(temp_buf, "%.*f°C/--", 0, high);
} else {
sprintf(temp_buf, "%.*f/%.*f°C", 0, high, 0, low);
}
it.printf(dx + 44, dy + 60, id(font_merriweather_400_14), color_on, TextAlign::TOP_CENTER, "%s", temp_buf);
}
}
// widget:template_sensor_bar id:w_mlc09ve9o96or type:template_sensor_bar x:561 y:440 w:239 h:40 wifi:true temp:false hum:false bat:true bg:true bg_color:white radius:8 border:0 icon_size:24 font_size:16 color:theme_auto wifi_ent:"" temp_ent:"" temp_unit:°C hum_ent:"" bat_ent:""
{
auto draw_filled_rrect = [&](int x, int y, int w, int h, int r, auto c) {
it.filled_rectangle(x + r, y, w - 2 * r, h, c);
it.filled_rectangle(x, y + r, r, h - 2 * r, c);
it.filled_rectangle(x + w - r, y + r, r, h - 2 * r, c);
it.filled_circle(x + r, y + r, r, c);
it.filled_circle(x + w - r - 1, y + r, r, c);
it.filled_circle(x + r, y + h - r - 1, r, c);
it.filled_circle(x + w - r - 1, y + h - r - 1, r, c);
};
draw_filled_rrect(561, 440, 239, 40, 8, COLOR_WHITE);
{
const char* wifi_icon = "\U000F092B";
if (id(wifi_signal_dbm).has_state()) {
float sig = id(wifi_signal_dbm).state;
if (sig >= -50) wifi_icon = "\U000F0928";
else if (sig >= -70) wifi_icon = "\U000F0925";
else if (sig >= -85) wifi_icon = "\U000F0922";
else wifi_icon = "\U000F091F";
}
it.printf(621 - 4, 460, id(font_material_design_icons_400_24), color_on, TextAlign::CENTER_RIGHT, "%s", wifi_icon);
if (id(wifi_signal_dbm).has_state()) it.printf(621 + 4, 460, id(font_roboto_400_16), color_on, TextAlign::CENTER_LEFT, "%.0fdB", id(wifi_signal_dbm).state);
else it.printf(621 + 4, 460, id(font_roboto_400_16), color_on, TextAlign::CENTER_LEFT, "--dB");
}
{
const char* bat_icon = "\U000F0082";
float lvl = id(battery_level).state;
if (lvl >= 90) bat_icon = "\U000F0079";
else if (lvl >= 50) bat_icon = "\U000F007E";
else if (lvl >= 20) bat_icon = "\U000F007B";
else bat_icon = "\U000F0083";
it.printf(740 - 4, 460, id(font_material_design_icons_400_24), color_on, TextAlign::CENTER_RIGHT, "%s", bat_icon);
if (id(battery_level).has_state()) it.printf(740 + 4, 460, id(font_roboto_400_16), color_on, TextAlign::CENTER_LEFT, "%.0f%%", id(battery_level).state);
else it.printf(740 + 4, 460, id(font_roboto_400_16), color_on, TextAlign::CENTER_LEFT, "--%%");
}
}
// widget:icon id:w_mld3tlt65a681 type:icon x:624 y:0 w:76 h:40 code:F050F size:40 color:theme_auto
it.printf(662, 20, id(font_material_design_icons_400_40), color_on, TextAlign::CENTER, "%s", "\U000F050F");
// widget:icon id:w_mld3xgl4eovrk type:icon x:700 y:0 w:100 h:40 code:F058E size:48 color:theme_auto
it.printf(750, 20, id(font_material_design_icons_400_48), color_on, TextAlign::CENTER, "%s", "\U000F058E");
// widget:text id:w_mld42ouedtkgm type:text x:400 y:40 w:200 h:40 text:"Greenhouse:"
it.printf(600, 80, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, "Greenhouse:");
// widget:text id:w_mld44ixnctkw9 type:text x:400 y:80 w:200 h:40 text:"Outside:"
it.printf(600, 120, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, "Outside:");
// widget:text id:w_mld44kyf68kc2 type:text x:400 y:120 w:200 h:40 text:"Kitchen:"
it.printf(600, 160, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, "Kitchen:");
// widget:text id:w_mld44mhtfw3k6 type:text x:400 y:160 w:200 h:40 text:"Lounge:"
it.printf(600, 200, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, "Lounge:");
// widget:text id:w_mld453mkkxkqk type:text x:400 y:200 w:200 h:40 text:"Study:"
it.printf(600, 240, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, "Study:");
// widget:text id:w_mld45bczo741j type:text x:400 y:280 w:200 h:40 text:"Basement:"
it.printf(600, 320, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, "Basement:");
// widget:text id:w_mld45bk147i8c type:text x:400 y:240 w:200 h:40 text:"Bathroom:"
it.printf(600, 280, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, "Bathroom:");
// widget:sensor_text id:w_mld4ann6l3yec type:sensor_text x:600 y:40 w:100 h:40 entity:"sensor.greenhouse_temperature" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.1f °C", id(sensor_greenhouse_temperature).state);
print_wrapped_text(700, 80, 100, 24, id(font_merriweather_700_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, wrap_buf);
}
// widget:sensor_text id:w_mld4c74g791ft type:sensor_text x:600 y:80 w:100 h:40 entity:"sensor.outside_temperature" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.1f °C", id(sensor_outside_temperature).state);
print_wrapped_text(700, 120, 100, 24, id(font_merriweather_700_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, wrap_buf);
}
// widget:sensor_text id:w_mld4c8iykfrts type:sensor_text x:600 y:120 w:100 h:40 entity:"sensor.kitchen_temp_temperature" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.1f °C", id(sensor_kitchen_temp_temperature).state);
print_wrapped_text(700, 160, 100, 24, id(font_merriweather_700_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, wrap_buf);
}
// widget:sensor_text id:w_mld4cakoy0cbk type:sensor_text x:600 y:160 w:100 h:40 entity:"sensor.lounge_temp_temperature" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.1f °C", id(sensor_lounge_temp_temperature).state);
print_wrapped_text(700, 200, 100, 24, id(font_merriweather_700_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, wrap_buf);
}
// widget:sensor_text id:w_mld4ccet3m3pq type:sensor_text x:600 y:200 w:100 h:40 entity:"sensor.0x00158d00044a3bca_temperature" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.1f °C", id(sensor_0x00158d00044a3bca_temperature).state);
print_wrapped_text(700, 240, 100, 24, id(font_merriweather_700_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, wrap_buf);
}
// widget:sensor_text id:w_mld4ce9zdqxas type:sensor_text x:600 y:240 w:100 h:40 entity:"sensor.bathroom_temperature" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.1f °C", id(sensor_bathroom_temperature).state);
print_wrapped_text(700, 280, 100, 24, id(font_merriweather_700_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, wrap_buf);
}
// widget:sensor_text id:w_mld4cgcwvt5pa type:sensor_text x:600 y:280 w:100 h:40 entity:"sensor.basement_sensor_temperature" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.1f °C", id(sensor_basement_sensor_temperature).state);
print_wrapped_text(700, 320, 100, 24, id(font_merriweather_700_20), COLOR_BLACK, TextAlign::BOTTOM_RIGHT, wrap_buf);
}
// widget:sensor_text id:w_mld4cjzxpxjxo type:sensor_text x:700 y:40 w:100 h:40 entity:"sensor.greenhouse_humidity" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.0f %%", id(sensor_greenhouse_humidity).state);
print_wrapped_text(750, 80, 100, 24, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_CENTER, wrap_buf);
}
// widget:sensor_text id:w_mld4dkrhuqri5 type:sensor_text x:700 y:80 w:100 h:40 entity:"sensor.outside_humidity" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.0f %%", id(sensor_outside_humidity).state);
print_wrapped_text(750, 120, 100, 24, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_CENTER, wrap_buf);
}
// widget:sensor_text id:w_mld4dmhhmkb7l type:sensor_text x:700 y:120 w:100 h:40 entity:"sensor.kitchen_temp_humidity" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.0f %%", id(sensor_kitchen_temp_humidity).state);
print_wrapped_text(750, 160, 100, 24, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_CENTER, wrap_buf);
}
// widget:sensor_text id:w_mld4do3snz0sb type:sensor_text x:700 y:160 w:100 h:40 entity:"sensor.lounge_temp_humidity" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.0f %%", id(sensor_lounge_temp_humidity).state);
print_wrapped_text(750, 200, 100, 24, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_CENTER, wrap_buf);
}
// widget:sensor_text id:w_mld4dpvikcfls type:sensor_text x:700 y:200 w:100 h:40 entity:"sensor.0x00158d00044a3bca_humidity" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.0f %%", id(sensor_0x00158d00044a3bca_humidity).state);
print_wrapped_text(750, 240, 100, 24, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_CENTER, wrap_buf);
}
// widget:sensor_text id:w_mld4drha6hesp type:sensor_text x:700 y:240 w:100 h:40 entity:"sensor.bathroom_humidity" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.0f %%", id(sensor_bathroom_humidity).state);
print_wrapped_text(750, 280, 100, 24, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_CENTER, wrap_buf);
}
// widget:sensor_text id:w_mld4dt52qcfqj type:sensor_text x:700 y:280 w:100 h:40 entity:"sensor.basement_sensor_humidity" format:"value_only"
{
char wrap_buf[512];
sprintf(wrap_buf, "%.0f %%", id(sensor_basement_sensor_humidity).state);
print_wrapped_text(750, 320, 100, 24, id(font_merriweather_400_20), COLOR_BLACK, TextAlign::BOTTOM_CENTER, wrap_buf);
}
// widget:datetime id:w_mld4j5m6nl5in type:datetime x:200 y:440 w:100 h:40 fmt:time_only
{
auto now = id(ha_time).now();
it.strftime(250, 460, id(font_merriweather_700_20), COLOR_BLACK, TextAlign::CENTER, "%H:%M", now);
}
// widget:text id:w_mld4jx8m7hsak type:text x:100 y:448 w:100 h:32 text:"Last Update:"
it.printf(200, 448, id(font_merriweather_400_14), COLOR_BLACK, TextAlign::TOP_RIGHT, "Last Update:");
// widget:datetime id:w_mld4mzlmaxim7 type:datetime x:300 y:448 w:146 h:32 fmt:weekday_day_month
{
auto now = id(ha_time).now();
it.strftime(373, 464, id(font_merriweather_400_14), COLOR_BLACK, TextAlign::CENTER, "%A %d %B", now);
}
// widget:sensor_text id:w_mlemgyyl0bmck type:sensor_text x:664 y:340 w:40 h:40 entity:"sensor.tautulli_watching" format:"value_only_no_unit"
it.printf(684, 340, id(font_merriweather_700_20), color_on, TextAlign::TOP_CENTER, "%.0f", id(sensor_tautulli_watching).state);
// widget:sensor_text id:w_mlemijhr7csl8 type:sensor_text x:662 y:380 w:40 h:40 entity:"sensor.tautulli_direct_streams" format:"value_only_no_unit"
it.printf(682, 420, id(font_merriweather_700_20), color_on, TextAlign::BOTTOM_CENTER, "%.0f", id(sensor_tautulli_direct_streams).state);
// widget:sensor_text id:w_mlemimeg99bku type:sensor_text x:750 y:379 w:40 h:40 entity:"sensor.tautulli_direct_plays" format:"value_only_no_unit"
it.printf(770, 419, id(font_merriweather_700_20), color_on, TextAlign::BOTTOM_CENTER, "%.0f", id(sensor_tautulli_direct_plays).state);
// widget:sensor_text id:w_mlemip10off8n type:sensor_text x:750 y:339 w:40 h:40 entity:"sensor.tautulli_transcodes" format:"value_only_no_unit"
it.printf(770, 339, id(font_merriweather_700_20), color_on, TextAlign::TOP_CENTER, "%.0f", id(sensor_tautulli_transcodes).state);
// widget:text id:w_mlemldcnwh916 type:text x:623 y:340 w:40 h:40 text:"Tot"
it.printf(643, 340, id(font_merriweather_400_20), color_on, TextAlign::TOP_CENTER, "Tot");
// widget:text id:w_mlemnsloiv1cj type:text x:710 y:339 w:40 h:40 text:"TC"
it.printf(730, 339, id(font_merriweather_400_20), color_on, TextAlign::TOP_CENTER, "TC");
// widget:text id:w_mlemp6nkwu3aw type:text x:710 y:379 w:40 h:40 text:"DP"
it.printf(730, 419, id(font_merriweather_400_20), color_on, TextAlign::BOTTOM_CENTER, "DP");
// widget:text id:w_mlempmwm7596a type:text x:622 y:379 w:40 h:40 text:"DS"
it.printf(642, 419, id(font_merriweather_400_20), color_on, TextAlign::BOTTOM_CENTER, "DS");
// widget:line id:w_mlemjs2hojo95 type:line x:623 y:380 w:161 h:1 stroke:1 color:theme_auto orientation:horizontal
it.filled_rectangle(623, 380, 161, 1, color_on);
// widget:line id:w_mlemk41imk319 type:line x:704 y:343 w:1 h:73 stroke:1 color:theme_auto orientation:vertical
it.filled_rectangle(704, 343, 1, 73, color_on);
// widget:icon id:w_mlemvyblp0xb1 type:icon x:547 y:350 w:60 h:60 code:F06BA size:48 color:theme_auto
it.printf(577, 380, id(font_material_design_icons_400_48), color_on, TextAlign::CENTER, "%s", "\U000F06BA");
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels