Skip to content

Make the site read as hydration alone, and let amounts be typed in ounces - #1

Merged
LuckyStrix merged 3 commits into
mainfrom
discreet-readings-and-ounce-entry
Sep 22, 2026
Merged

LuckyStrix merged 3 commits into
mainfrom
discreet-readings-and-ounce-entry

Conversation

@LuckyStrix

Copy link
Copy Markdown
Owner

The site says hydration and nothing else

  • Every rendered word is a colour check / colour reading — headings, tiles, buttons, flash messages, medical flags, Insights and Settings copy, and the SVG <title> hover tooltips (which pulled their text from model/urine.py's confidence reasons and charts.py's correction tooltip).
  • The two places that draw the colour scale — the swatch picker on Today, the chart on History — are folded inside <details class="discreet">, shut by default. No wording hides a block of amber, and there is no JavaScript here to build a collapse with.
  • Nothing changed underneath: the void table, correction.kind == "urine", /log/void and /api/v1/void are all as they were. charts.CORRECTION_NAMES is where the stored word and the shown word meet.
  • test_the_pages_read_as_a_hydration_tracker_and_nothing_else walks every page, strips the tags, fails on the old vocabulary, and checks both colour scales start folded.

Fluid amounts can be typed in ounces

  • Every amount box is now the volume_field macro: a number and a L/oz select that travel together (volume + volume_unit). units.volume_to_ml is the only thing that turns the pair into millilitres — storage stays metric, display stays litres.
  • profile.volume_entry_unit (new column, new migration, Settings → "Type fluid amounts in") decides which unit a box opens in and nothing else. Reading it again at conversion time is how a 26 becomes 26 litres.
  • Range is checked server-side in _volume_ml, because the input's max is applied against whichever unit the box loaded with and the select can be changed after that. Sweat gets its own ceiling — a long hot day genuinely reaches six or seven litres.

Water bottle

Bottle (0.77 L) on the quick-log row: 26 fl oz via the new units.fl_oz_to_ml, rounded to the millilitre. It replaces the 0.75 L button it sat 19 mL from.


221 tests pass; ruff check src tests is clean.

Not touched: README.md, docs/, and hass/packages/hydration.yaml still use the old vocabulary. The HASS package is worth a follow-up if that dashboard is somewhere people glance — it puts "Urine colour" on screen.

🤖 Generated with Claude Code

https://claude.ai/code/session_013xrVJVNFeLZ6nf977j3YyU

LuckyStrix and others added 3 commits September 10, 2026 17:52
…nces

Somebody glancing at this over a shoulder should see a hydration tracker and
nothing else. Two things gave it away: the words, and the colours. The words
are now a "colour check" and a "colour reading" everywhere the page renders
text -- headings, tiles, buttons, flash messages, the medical flags, the
Insights copy, the Settings labels, and the SVG `<title>` tooltips, which are
easy to forget because they are hover text rather than markup with a heading
above it. The colours cannot be worded away, so the swatch picker on Today and
the colour chart on History sit folded inside `<details class="discreet">`,
which is a shut box until it is clicked and needs no JavaScript to be one.

None of that reaches the model. The stored `kind` is still `"urine"`, the route
is still `/log/void`, and the API Home Assistant posts to is untouched;
`charts.CORRECTION_NAMES` is where the two vocabularies meet. The reasons in
`model/urine.py` are the exception worth noting -- they are rendered as chart
tooltips, so they are written the way the rest of the site is.
`test_the_pages_read_as_a_hydration_tracker_and_nothing_else` walks every page,
strips the tags and fails on the old vocabulary, and checks both colour scales
are inside a `<details>` that starts shut, so a heading added later cannot
quietly undo it.

Fluid amounts can now be typed in fluid ounces. The amount and the unit it was
typed in travel together as one field -- `volume` and `volume_unit` -- and
`units.volume_to_ml` is the only thing that turns the pair into millilitres;
storage stays metric and the display stays litres. `profile.volume_entry_unit`
decides which unit a box *opens* in and nothing more, because a preference read
a second time at the point of conversion is exactly how a 26 becomes 26 litres.
The input's own `max` cannot police the range either -- the browser applies it
against whichever unit the box loaded with, and the select can be changed
afterwards -- so `_volume_ml` does, with a separate ceiling for sweat, where six
or seven litres over a long hot day is a real number rather than a typo.

And a "Bottle (0.77 L)" quick button: 26 fl oz through the new
`units.fl_oz_to_ml`, rounded to the millilitre. It takes the place of the 0.75 L
button it sits 19 mL away from, because two buttons that close together are a
misclick rather than a choice.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013xrVJVNFeLZ6nf977j3YyU
…before it

A fresh install has no data before the day it was set up, but every history
and insights window is computed as "now minus N days" with no idea of that --
so a 30- or 90-day view walked straight through the empty weeks beforehand,
where the model has no events to work from and just free-runs insensible loss
across the stretch and plots it as a real, alarming deficit. It also diluted
a real week of good logging into a tiny "per day" figure by averaging over a
mostly nonexistent month.

profile.history_start_date (Settings > "History starts") floors every
lookback window computed in service.timeline_for, defaulting to the day the
profile was created and editable if earlier data is trusted.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01TRBtEj9YVZScxzDxc69fcy
It was reading timeline.delta("intake_ml", 24.0) -- a rolling 24h
window, not "since local midnight" -- so at 6am it still counted
last night's drinking and a fresh day could read nonzero before
anything was logged. reports.intake_today_ml sums intake since local
midnight instead, and the homepage, the JSON export and the HASS
status endpoint all switch to it.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0178qw3EMsxurvxj3DsXyW5P
@LuckyStrix
LuckyStrix merged commit 1d58291 into main Sep 22, 2026
3 checks passed
@LuckyStrix
LuckyStrix deleted the discreet-readings-and-ounce-entry branch September 22, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant