Files
homeassistant_espscreen/components/smart_display/cyd_ui.h
T
Max Gramser 2667f41f6f Direct controls on wide cards, weather/climate cards, spinner busy state (0.2.23)
App 0.2.23 / firmware 0.2.19.

- Wide cards of climate, switch, light, fan, vacuum, cover, media_player,
  number, select, timer, scene, script and button show Home Assistant style
  controls on the right (keys, -/+ pill with debounced send, slider with
  handle, toggle, run key); per-tile choice `options.controls` in the editor
  with mockup preview; the add-on sends only the set a card really shows.
- Weather card: now + next hours and coming days with rain, from daily and
  hourly forecasts; scenes/scripts/buttons show when they last ran.
- Climate card: power key, tap-to-toggle option, fan and swing rows in a
  redesigned mode picker (grey backdrop, white chips, accent selection).
- Busy state as a spinner on a translucent sheet; render guards so only
  changed styles reach LVGL; dark theme removed from the runtime code.
- supported_features above 1e6 reaches the screen; middle dot in text fonts;
  18 control glyphs in the icon fonts; diagnostics services
  preview_climate_picker and preview_busy; demo layout --controls.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-13 15:02:15 +02:00

65 lines
2.1 KiB
C++

#pragma once
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <string>
namespace cyd {
// The n-th quoted item of a JSON list such as ["auto","low","high"]; empty when absent.
inline std::string list_item(const std::string &json, unsigned index) {
size_t pos = 0; unsigned n = 0;
while ((pos = json.find('"', pos)) != std::string::npos) {
size_t end = json.find('"', pos + 1);
if (end == std::string::npos) return {};
if (n++ == index) return json.substr(pos + 1, end - pos - 1);
pos = end + 1;
}
return {};
}
// Work in tenths of a degree and always clamp AFTER rounding.
inline int quantize_temperature(int value, int minimum, int maximum, int step) {
step = std::max(1, step);
value = std::clamp(value, minimum, maximum);
const int snapped = minimum + static_cast<int>(std::lround(
static_cast<double>(value - minimum) / step)) * step;
return std::clamp(snapped, minimum, maximum);
}
class TouchGuard {
public:
void begin(uint32_t now, int x = 0, int y = 0) {
started_ = now;
accepted_ = false;
moved_ = false;
start_x_ = x;
start_y_ = y;
}
void update(int x, int y) {
// A contact that jumps to another target must never become a tile tap.
if (std::abs(x - start_x_) > 18 || std::abs(y - start_y_) > 18) moved_ = true;
}
void consume() { accepted_ = true; moved_ = true; }
bool accept(uint32_t now, int tile) {
return !moved_ && accept_slider(now, tile);
}
// Only for a slider which captured this contact and did not lose the press.
// Consume the gesture so its parent can never also turn it into a tile tap.
bool accept_slider(uint32_t now, int tile) {
if (accepted_ || now - started_ < 60) return false;
if (has_previous_ && tile == previous_tile_ && now - previous_ < 600) return false;
accepted_ = true;
has_previous_ = true;
previous_tile_ = tile;
previous_ = now;
return true;
}
private:
uint32_t started_{0}, previous_{0};
int previous_tile_{-1};
int start_x_{0}, start_y_{0};
bool moved_{false};
bool accepted_{false}, has_previous_{false};
};
inline TouchGuard touch_guard;
} // namespace cyd