Skip to content

Commit 85a9711

Browse files
authored
Merge pull request #22 from GuvHas/feature/interactive-dashboard-ux
Rebuild the dashboard around drink-window filters, and cut 0.0.20 The page was a sortable table with a search box. On a phone, with 151 bottles, that could not answer the only question a cellar app exists for. - Four filter chips with live counts: All wines, Ready to drink, Past window, Needs aging, in a sticky header with the search box - Rows became expandable cards; the drawer carries the barcode, the full window, Copy bin and a link to CellarTracker - The drink window is a bar showing where this year sits in it, plus a label, so colour is never the only signal - Search covers wine, vintage, location, bin and barcode, all tokens - Sort by six columns either way, with unrecorded values last The last year of a window now reads "Drink this year" rather than red. The page had painted it red since the beginning, which reads as expired, while the coordinator counted it as ready - the two disagreed about the same bottle. test_dashboard_agrees_with_sensors keeps them agreeing. Barcode joins the compact projection so the page can search and show it. 86 new tests over the page's own script under node, plus a README audit: eight claims in it had gone stale, two of which are now pinned by tests that read the page and COMPACT_FIELDS rather than restating them.
2 parents e5bf25b + 663207d commit 85a9711

11 files changed

Lines changed: 2047 additions & 405 deletions

README.md

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
# CellarTracker for Home Assistant
55

66
Brings your [CellarTracker](https://cellartracker.com) wine cellar into Home Assistant: summary
7-
sensors for bottle count and cellar value, plus a searchable, sortable dashboard of every bottle.
7+
sensors for bottle count, cellar value and drinking windows, plus a dashboard that filters every
8+
bottle down to the ones you can drink tonight.
89

910
> **Disclaimer**
1011
> This is a personal project. It is not affiliated with, connected to, or endorsed by
@@ -45,11 +46,14 @@ and error semantics; its own `requests`-based transport sets no timeout and is n
4546

4647
- Two summary sensors — total bottle count and total cellar value — with proper device classes,
4748
units and state classes, so both feed Home Assistant's long-term statistics.
49+
- Two drink-window sensors — how many bottles are ready to drink, and how many are past their
50+
window — derived at poll time, so they cost nothing and add no entity per bottle.
4851
- A selectable currency, so the value sensor is denominated correctly.
49-
- A diagnostic status sensor.
52+
- A diagnostic sensor reporting when the cellar last synchronised.
5053
- A REST endpoint exposing full per-bottle detail, and a self-contained dashboard page that
51-
renders it as a searchable, sortable table with drink-window highlighting. The page ships with
52-
the integration and is served from it, so there is nothing to copy into `<config>/www`.
54+
filters it by drinking window, searches it across five fields and shows each bottle's position
55+
in its window. The page ships with the integration and is served from it, so there is nothing
56+
to copy into `<config>/www`.
5357
- Reauthentication: if your password changes, Home Assistant prompts you to re-enter it rather
5458
than silently failing.
5559
- One account per installation, enforced by the config flow, so there is no ambiguity
@@ -108,7 +112,7 @@ state attributes:
108112

109113
```
110114
GET /api/cellartracker/inventory # every bottle, as JSON
111-
GET /api/cellartracker/inventory?view=compact # the same bottles, nine columns
115+
GET /api/cellartracker/inventory?view=compact # the same bottles, ten columns
112116
GET /api/cellartracker/settings # the configured currency and its symbol
113117
```
114118

@@ -127,6 +131,7 @@ Each bottle in the response carries CellarTracker's own column names. The ones t
127131
| `Vintage` | Vintage year, `0` for non-vintage |
128132
| `Producer` | Producer name |
129133
| `Location` / `Bin` | Where the bottle is stored |
134+
| `Barcode` | The bottle's barcode, if you have scanned one |
130135
| `Size` | Bottle size, e.g. `750ml` |
131136
| `Valuation` | Current value, coerced to a float (`0.0` if unparseable) |
132137
| `Price` | What you paid |
@@ -269,9 +274,41 @@ title: My Wine Collection
269274
The page reads your live Home Assistant session from the parent frame, so it needs no token or
270275
credential of its own. Open it embedded in a dashboard, not as a standalone browser tab.
271276
272-
It gives you search across wine name, location and bin; sortable columns; bottle values formatted
273-
in your configured currency; links to each wine on CellarTracker; drink-window colouring (green =
274-
ready, red = too early or past); and light/dark theme following your Home Assistant theme.
277+
### What it gives you
278+
279+
**Four filter chips**, each carrying its count, so a 150-bottle cellar becomes a short list:
280+
281+
| Chip | Shows |
282+
|---|---|
283+
| **All wines** | Everything, including bottles with no recorded window |
284+
| **Ready to drink** | The current year falls inside the drinking window |
285+
| **Past window** | The window ended before this year |
286+
| **Needs aging** | The window has not opened yet |
287+
288+
The Ready and Past counts are the same numbers the sensors report — they are checked against each
289+
other by the test suite, so the chip and the sensor card beside it cannot disagree.
290+
291+
**Search** across wine name, vintage, location, bin and barcode. Every word has to match, so
292+
`chianti 2023` narrows rather than finding nothing.
293+
294+
**Sort** by wine, vintage, value, drink-by year, bin or location, in either direction. Bottles
295+
with nothing recorded for the chosen column sort last rather than heading the list.
296+
297+
**Each bottle** shows its name, vintage, location and bin, its value in your configured currency,
298+
and a bar showing where this year sits inside its drinking window:
299+
300+
| Label | Meaning |
301+
|---|---|
302+
| **Ready** (green) | Inside the drinking window |
303+
| **Drink this year** (amber) | The final year of the window — urgent, not expired |
304+
| **Past window** (red) | The window ended before this year |
305+
| **Needs aging** (blue) | Not open yet |
306+
| **No window** (grey) | CellarTracker has no recommendation for this bottle |
307+
308+
Tapping a bottle opens a drawer with its barcode, full window, value, a **Copy bin** button and a
309+
link to the wine on CellarTracker.
310+
311+
The page follows your Home Assistant light/dark theme, and every control is sized for a thumb.
275312

276313
The card needs no account parameter — one account is supported per installation, so the
277314
endpoints have nothing to disambiguate. A stale `?entry_id=...` left over from a card configured
@@ -300,8 +337,10 @@ entities:
300337
name: Bottles
301338
- entity: sensor.<account>_total_value
302339
name: Cellar value
303-
- entity: sensor.<account>_status
304-
name: Connection
340+
- entity: sensor.<account>_ready_to_drink
341+
name: Ready to drink
342+
- entity: sensor.<account>_last_synchronised
343+
name: Last synchronised
305344
```
306345

307346
### Markdown card with an average
@@ -419,13 +458,18 @@ window. **That is not possible with the entities this integration creates**, bec
419458
per-bottle entities to filter — `auto-entities` works over the entity registry, and bottles are
420459
not in it.
421460

422-
Drink-window filtering happens in the dashboard page instead, which colours `BeginConsume` and
423-
`EndConsume` per bottle: green when the year is in range, red when the bottle is too young or
424-
past its window. Sort by either column to bring the relevant bottles together.
461+
There are two answers that do not need per-bottle entities.
462+
463+
**For a count**, use the sensors: `sensor.<account>_ready_to_drink` and
464+
`sensor.<account>_past_drinking_window` are ordinary numeric entities, so they work in any card,
465+
template or automation.
425466

426-
If you want drink-window data in Lovelace proper, the missing piece is per-bottle entities — see
427-
[Bottle-level data](#bottle-level-data) for why they are not created by default. Please open an
428-
issue if this matters to you; it is a reasonable feature to add behind an opt-in, given the
467+
**For the actual list**, use the dashboard page and its **Ready to drink** chip. That is what the
468+
chips are for, and the counts match the sensors exactly.
469+
470+
If you want the bottles themselves in Lovelace proper, the missing piece is per-bottle entities —
471+
see [Bottle-level data](#bottle-level-data) for why they are not created by default. Please open
472+
an issue if this matters to you; it is a reasonable feature to add behind an opt-in, given the
429473
recorder cost is the user's to accept.
430474

431475
---
@@ -519,16 +563,28 @@ logger:
519563

520564
```bash
521565
pip install -r requirements_test.txt
522-
python -m pytest # 149 tests
566+
python -m pytest
523567
ruff check .
524568
```
525569

526570
The test suite stubs the handful of `homeassistant` symbols the integration imports rather than
527-
depending on `pytest-homeassistant-custom-component`, so it installs in seconds and runs in under
528-
a second. The dashboard tests execute `cellar.html`'s real script under Node, so install Node to
529-
run them — they skip if it is absent.
571+
depending on `pytest-homeassistant-custom-component`, so it installs in seconds and runs in a few.
572+
The dashboard tests execute `cellar.html`'s real script under Node, so install Node to run them —
573+
they skip if it is absent.
574+
575+
Type checking needs Home Assistant itself, which the test suite deliberately does not install:
576+
577+
```bash
578+
python3.13 -m venv .typecheck
579+
.typecheck/bin/pip install -r requirements_mypy.txt
580+
.typecheck/bin/python -m mypy
581+
```
582+
583+
Running `mypy` against an interpreter without Home Assistant is worse than not running it: every
584+
`homeassistant.*` import resolves to `Any`, and the check passes over code it never looked at.
585+
The configuration refuses to do that, so it will fail loudly rather than mislead you.
530586

531-
CI runs the suite on Python 3.12 and 3.13, plus `ruff`, `hassfest` and HACS validation.
587+
CI runs the suite on Python 3.12 and 3.13, plus `ruff`, `mypy`, `hassfest` and HACS validation.
532588

533589
### Cutting a release
534590

custom_components/cellar_tracker/const.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
"Vintage",
2626
"Location",
2727
"Bin",
28+
# The dashboard searches it and shows it in a bottle's drawer, which is
29+
# what a phone at the rack is for. It is one short string per row, and it
30+
# already identifies a bottle to the coordinator (IDENTITY_FIELDS).
31+
"Barcode",
2832
"BeginConsume",
2933
"EndConsume",
3034
"Valuation",

custom_components/cellar_tracker/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
"requirements": [
1616
"cellartracker==1.1.1"
1717
],
18-
"version": "0.0.19"
18+
"version": "0.0.20"
1919
}

0 commit comments

Comments
 (0)