Skip to content

ADR-0051: The zone left of the clock shows a content chosen in the blueprint — the voice controls, a chart of the coming hours or a compact music player, the next one by a tap on the second temperature

Status: Accepted (2026-10-10, asked for by the author; not tried on a tablet when written). Completed the same day by a second lot: the compact music player and a restyled chart (sections « Lot 2 » below). Date: 2026-10-10

Context

On the home page, the column left of the clock (x 20–425, above the « Ok Nabu » frame at y 236, ADR-0041) always showed the voice controls: the microphone (icon_mic_status, tap to talk, long press for the assistant popup) and the Domo / Discu buttons (hidden without a discussion pipeline, zone discussion).

The author asked (2026-10-10) to choose what this zone shows: the voice controls, an audio player, or a chart — « very beautiful, light » — and to handle it like the other zones (« Ok Nabu » panel, row under the clock, − / + tile): the next content by a tap on a given area of the screen (for now the second temperature, whose tap opened the Arcade), and the same action among the gesture choices.

A music player is being built at the same time by another lot (ADR-0050, popup and mini bar, not merged when this was written).

Decision

  • Three contents. ZoneGauche (Tab5/socle/tab5_parse.h, pure C++): VOCAL (the screen from before), GRAPHIQUE (the chart below), LECTEUR (the compact music player, lot 2 below, built on ADR-0050's data). In the first lot the firmware read, kept and skipped lecteur, and the blueprint accepted the code without offering it; the second lot added its case in disponible(), its widgets and the option. tests/test_zone_gauche.py keeps the options equal to what the screen can show.
  • The voice controls do not change. The four widgets go into one transparent container, zone_vocal, at the corner of the screen, without padding or border: their screen coordinates, look, gestures and the discussion masking stay the same (the container is hidden or shown as a whole; the per-widget hidden flags are untouched).
  • The chart: the coming hours, from data already on the tablet. cal_heures_data (15 slots, pushed by tab5_maj_previsions_heures_bulk; nothing new from Home Assistant, contract unchanged): the monotone curve of the temperatures (the Weather popup's own smoothing, moved to ui_courbe_lisse(), ADR-0043) in the colour of their mean, a dot and its value at the warmest and the coldest hour (one only when flat), rain bars in mm in the rain colours, three hour labels (now, then every 4 slots) and the rain total on the same line. About 30 objects (lv_line + lv_obj: lv_chart is disabled), built at its first appearance, repainted only when new hourly forecasts arrive or the theme changes — at once if shown, at its next appearance otherwise. Colours from the palette (UIColor, no new role), fonts already compiled (roboto_22), no new screen text (« En attente de Home Assistant » exists). The card is zone_graphique (405 × 184 at x 20, y 42: 8 px under the status bar's ink, 10 px above the « Ok Nabu » frame), in the glass of the column's buttons (style_clim_btn_page); its sides keep 22 px for a pill-shaped frame (Capsule theme). A tap on it opens the Weather popup.
  • The next content: the second temperature's tap, and a gesture. btn_serre_games (its id kept: docs, tests and the render cite it) now calls zone_gauche_suivante() on a tap; its long press (history, ADR-0032) does not change. Without a second sensor, the icon at its place was a gamepad (« the Arcade is here »); it is now view-carousel (U+F056C), the icon of the new gesture code zone_gauche_suivante (index 23, at the end of kCodesGestes: NVS keeps the index; mdi_font_26, mdi_font_45, mdi_font_70), selectable on any of the twelve home gestures (ADR-0039). The Arcade stays one touch away: the tap of the gamepad button (« auto ») and « Jeux » in the navigation wheel (ADR-0042).
  • Chosen in the blueprint, like the other zones. A section « Zone à gauche de l'horloge » (zone_gauche): the content at start and the contents the tap goes through (a multiple select). One key of tab5_maj_emplacements, gauche|start|c1|c2…;, pushed with the gestures (all states: connection, reload, « MAJ Écran », HA start); an older firmware ignores it. Read by zone_gauche_lire() (tab5_parse.cpp; cases in tools/test_parse.cpp, called by the libFuzzer harness): an unknown start is vocal, unknown codes are left out, the start is always in the cycle (alone, the tap changes nothing). Without the key (a blueprint from before): vocal at start, vocal and graphique on tap — the screen from before until the first tap.
  • Kept in NVS (« zgau », « ZGA1 »: current, start, cycle), so the content shown survives a restart. A new start from the blueprint shows at once; the same choices pushed again change nothing; a current content no longer offered falls back to the start. The order of the cycle is the enum's (vocal, chart, player).
  • Instant change, no animation (the author's preference).

Lot 2 (2026-10-10): the compact music player

  • What it shows: in a card with the chart's footprint and glass (zone_lecteur, Tab5/ui_components/lecteur_zone.yaml, x 20, y 42, 405 × 184, style_clim_btn_page), the cover on the left (the Music popup's online_image, the same decoded pixels: no second download, no second buffer), and on the right the title, the artist, a thin position bar (4 px, lv_bar, no knob: seeking stays in the popup) and previous / play-pause / next centred under them. Nothing playing: the popup's own sentence (« Rien en lecture », « Lecteur éteint », « Lecteur indisponible », « En attente de Home Assistant »: no new screen text) and the player's name, centred when no button applies. A button shows only when the player offers it (supported_features), at its place. The volume is left out: a slider in a 205 px column would be cramped, and the popup is one tap away.
  • Same data, same commands: the widgets are bound by tab5_lecteur_lier (also run at boot by tab5_zones_apply, so the player can be the start content before any push) and painted by tab5_lecteur.cpp (peindre_zone()), from the state of tab5_maj_lecteur; the buttons call lecteur_appui(), i.e. the esphome.tab5_lecteur event with the popup's actions. Nothing new from Home Assistant, no entity named, the contract unchanged. A tap elsewhere on the card opens the popup (Ecran::MUSIQUE).
  • Theme-aware geometry: the border and radius of the card are read at paint time; the cover's corner radius is the card's inner radius minus the margin (concentric corners, a near-circle in the Capsule theme), and the text and bar widths stop before the card's rounded corner.
  • Painted only when shown: lecteur_zone_montrer() (called by zone_gauche_appliquer()); while hidden, a push only marks it « dirty » and it is repainted at its next appearance. The position bar moves once a second while playing (lecteur_tic()), only while shown.
  • The « now playing » bar (ADR-0050) stays on the Ok Nabu frame, but hides while the zone shows the player (the author's choice) and comes back when the zone changes.
  • Skipped when no player is chosen: once Home Assistant has pushed an empty state (its list « Tab5 · lecteurs de musique » is empty), disponible() skips lecteur in the cycle and in the fallback; a list filled again brings it back (lecteur_recu() calls zone_gauche_appliquer() when this changes). Before any push it shows (« En attente de Home Assistant »), so a start on the player does not flicker at boot. The stored current content is not overwritten, so the player comes back by itself.
  • Blueprint: the option « Lecteur de musique · Music player » in both lists, and in the default cycle ([vocal, graphique, lecteur]): skipped anyway without players. The firmware's default without the key stays voice and chart (a blueprint from before).
  • Built at boot in YAML, not lazily in C++ (about 15 objects): every MDI glyph then sits on a YAML label with its font (code rule 9 can check it), the theme's glass and accent styles apply as role styles (code rule 1), and the off-device render needs nothing more. The chart stays lazy (its 30 objects have no MDI glyph nor role style).

Lot 2 (2026-10-10): the chart restyled

Asked by the author on the render's capture (line too thin, the « 16° » label touching the first point, weak rain shown as a row of dots):

  • the curve is 5 px wide with round caps, over a faint gradient in its colour (about 33 % under the line down to 0 at the rain baseline). lv_canvas, lv_chart and the drawing of A8 images are disabled in the build (lv_conf.h), so it is one ARGB8888 lv_image whose pixels are computed when the chart is painted (new hourly forecasts or a theme, about 36 000 pixels, at most once an hour); its buffer (363 × 110 × 4 bytes, 156 KiB: the curve's span in the card) is taken once in PSRAM. One object; blending it costs a redraw of that area only when the card is redrawn;
  • the warmest and coldest dots are 12 px with a 3 px ring in the background colour; each value goes to the first free place around its dot (above first for the warmest, below for the coldest, then diagonals, sides, the other side): never over the curve (its line plus 3 px), a rain bar, the other value or dot, nor outside the card;
  • rain bars readable even when weak: any forecast rain above 0 gets a bar at least 8 px high, the strongest 26 px, on a square-root scale (a light rain next to a shower does not fall back to the minimum); no rain, no bar.

Rejected

  • Building the player in this lot: the other lot is writing the player (state, commands, popup) right now; a second player here would be a copy to merge later (rule 5). The compact player will read its data.
  • Leaving lecteur out of the enum until the second lot: the NVS and the key would then need a second format; reserving it now costs one value and one code.
  • Offering lecteur in the blueprint already (first lot): a choice that showed nothing on the screen. Done in lot 2.
  • Removing the « now playing » bar once the player can sit left of the clock (lot 2): the author keeps it for the other contents.
  • The volume in the compact player (lot 2): it does not fit cleanly in the column; the popup has it.
  • A service variable or an event: a key of tab5_maj_emplacements is what the gestures, defil and the solar icon already use; the contract between versions does not change (contrat/contrat.yaml untouched).
  • Rain probabilities: cal_heures_data carries the quantity (mm) only; bars in mm, like the Weather popup.
  • Switching back to the voice controls during a voice session: not asked; with the chart shown, the listening state is not visible on the home page (the assistant popup and the voice reply still are). Left to the author.

Consequences

  • The tap on the second temperature no longer opens the Arcade (docs/arcade.md, the user manual and docs/screens.md say so); the render opens the Arcade through the gamepad button (tools/rendu/ecrans.py) and captures the chart (accueil-zone-graphique, and in the Capsule theme).
  • A new content: its value at the end of ZoneGauche and its code (tab5_parse.h), its case in disponible() and zone_gauche_appliquer(), its option in the blueprint; tests/test_zone_gauche.py compares.
  • Lot 2: three more render captures (accueil-zone-lecteur, accueil-zone-lecteur-vide, accueil-zone-lecteur-gelule); the chart's captures change (gradient, labels, rain bars).
  • Not tried on a tablet when written: the author judges the look and the touch.