inline int measure_width(const char* text, esphome::display::BaseFont& font) { int width; int x_offset; int baseline; int height; font.measure(text, &width, &x_offset, &baseline, &height); return width; } inline int measure_baseline(const char* text, esphome::display::BaseFont& font) { int width; int x_offset; int baseline; int height; font.measure(text, &width, &x_offset, &baseline, &height); return baseline; } inline void render_sensor(esphome::display::Display& it, int x, int y, float value, std::string unit, const std::string& label) { int normal_height = measure_baseline("X", id(normal_font)); char formatted[20]; if (std::isnan(value)) { it.print(x, y, &id(normal_font), TextAlign::TOP_CENTER, "--"); } else { char value_text[20]; if (std::abs(value) >= 2000) { unit = "k" + unit; value /= 1000; } if (std::abs(value) < 10) { sprintf(value_text, "%.1f", value); } else { sprintf(value_text, "%.0f", value); } int value_width = measure_width(value_text, id(normal_font)); int unit_width = measure_width(unit.c_str(), id(small_font)); int width = value_width + unit_width + 3; it.print(x - width / 2, y + normal_height, &id(normal_font), TextAlign::BASELINE_LEFT, value_text); it.print(x + width / 2 - unit_width, y + normal_height, &id(small_font), TextAlign::BASELINE_LEFT, unit.c_str()); } it.print(x, y + normal_height + 3, &id(small_font), TextAlign::TOP_CENTER, label.c_str()); } inline void render(esphome::display::Display& it) { constexpr int kHeight = 540; constexpr int kWidth = 960; // Clock ESPTime now = id(rtc_time).now(); float seconds = now.second + now.minute * 60 + now.hour * 3600; int rounded_minutes = static_cast(std::roundf(seconds / 60 / 5)) * 5; it.printf(15, 8, &id(clock_font), TextAlign::TOP_LEFT, "%d:%02d", rounded_minutes / 60, rounded_minutes % 60); float outside = (!std::isnan(id(ruuvi_outside_temperature).state) ? id(ruuvi_outside_temperature).state : id(outside_temperature).state); int right_width = measure_width("123||", id(normal_font)); render_sensor(it, kWidth - right_width / 2, kHeight * 7 / 100, outside, "°C", "Outside"); render_sensor(it, kWidth - right_width / 2, kHeight * 58 / 100, id(scd40_temperature).state, "°C", "Inside"); int bottom_width = measure_width("1999|", id(normal_font)); render_sensor(it, bottom_width / 2 + kWidth * 0 / 100, kHeight * 58 / 100, id(scd40_co2).state, "", "CO2 ppm"); render_sensor(it, bottom_width / 2 + kWidth * 33 / 100, kHeight * 58 / 100, id(solar_power).state, "W", "Solar"); static const struct { float level; std::string icon; } battery_levels[] = { {10, "mdi-battery-alert-variant-outline"}, {40, "mdi-battery-low"}, {70, "mdi-battery-medium"}, {std::numeric_limits::max(), "mdi-battery-high"}, }; // Battery float battery_level = id(m5paper_battery_level).state; if (!std::isnan(battery_level)) { for (const auto& level : battery_levels) { if (battery_level <= level.level) { it.print(910, 10, &id(battery_font), id(material_icons_map)[level.icon].c_str()); break; } } } }