Skip to content

Commit 5785f41

Browse files
committed
Light the rack from the cellar
The integration could tell you what to drink on a screen. The bottles are in a rack, so this is the same answer in the room: an ESPHome node that lights one block of LEDs per bin, and a Home Assistant package that turns the inventory into what it paints. A bin carries the drinking-window state the dashboard paints it with - ready, drink-this-year, past, aging, no window - so the rack and /cellartracker/cellar.html cannot disagree about a bottle. The rule is the same one _drink_window_counts uses; a test runs the template beside the coordinator over one cellar and compares, the way the dashboard's own counts are checked. The whole rack travels as 169 state characters, one per bin, which is a single action call and fits inside a Home Assistant state. The node therefore knows nothing about wine: it composes colours once per call and each strand copies its own row out. A locator hides the overview rather than destroying it, so a poll landing while you are looking for a bottle does not take the answer away. Two things the rewrite had to settle, both found by compiling it: * `bit_bang` does not build on ESP32, so neopixelbus tops out at the ten strands its RMT channels and I2S buses can be pinned to. Thirteen rows needs FastLED, which takes a channel per strand as it transmits and hands it back. * Left without a `method`, every neopixelbus strand gets the same default and they fight over it - which is a rack whose rows mirror each other rather than a config that fails to compile. Credentials are `!secret` references throughout, and the `api:` block is on the `actions:`/`action:` spelling ESPHome moved to in 2024.8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011au3uaZneMFTbnssPSwpLt
1 parent 7a8e94c commit 5785f41

6 files changed

Lines changed: 1256 additions & 0 deletions

File tree

README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ bottle down to the ones you can drink tonight.
2323
- [Configuration](#configuration)
2424
- [The dashboard](#the-dashboard)
2525
- [Lovelace and automation examples](#lovelace-and-automation-examples)
26+
- [Lighting the rack](#lighting-the-rack)
2627
- [Troubleshooting and FAQ](#troubleshooting-and-faq)
2728
- [Development](#development)
2829
- [License](#license)
@@ -474,6 +475,76 @@ recorder cost is the user's to accept.
474475

475476
---
476477

478+
## Lighting the rack
479+
480+
If your bottles live in a rack you can put LEDs behind, the cellar can answer "what should I open"
481+
in the room rather than on a screen. `examples/` holds a working pair of configurations for that:
482+
483+
| File | What it is |
484+
|---|---|
485+
| `examples/esphome/winerack1led.yaml` | An ESPHome node driving one WS2812 strand per rack row, with a block of LEDs behind each bin |
486+
| `examples/home_assistant/wine_rack_leds.yaml` | The Home Assistant package that turns this integration's inventory into what the node paints |
487+
488+
The rack is a 13 x 13 grid: rows **A**-**M**, bins **1**-**13**, addressed the way CellarTracker
489+
addresses them — `Location` is the rack, `Bin` is the slot, so bin `A7` is row A, column 7. Both
490+
files are written for that shape and say where to change it.
491+
492+
### What it shows
493+
494+
Every lit bin carries the same drinking-window state the dashboard paints, so the rack and
495+
`/cellartracker/cellar.html` cannot tell you different things about the same bottle:
496+
497+
| Bin | Meaning |
498+
|---|---|
499+
| Green | **Ready** — this year is inside the drinking window |
500+
| Amber | **Drink this year** — the final year of the window |
501+
| Red | **Past window** — the window closed before this year |
502+
| Blue | **Needs aging** — the window has not opened yet |
503+
| Dim white | **No window** — CellarTracker has no recommendation for that bottle |
504+
| Dark | No bottle in that bin |
505+
506+
A bin usually holds several bottles. It shows the one that most wants dealing with: past, then
507+
drink-this-year, then ready, then aging, then a bottle with no window at all.
508+
509+
There is also a **locator** — one bin lit on its own with the rest of the rack dark, for finding a
510+
particular bottle. Copy a bin from the dashboard's bottle drawer, run the `wine_rack_1_locate`
511+
script, and walk to the rack. The overview comes back when the hold expires, and a poll that lands
512+
while you are looking does not steal the answer from you.
513+
514+
### How the two halves talk
515+
516+
The node exposes four actions, and knows nothing about wine:
517+
518+
| Action | Takes |
519+
|---|---|
520+
| `esphome.winerack1led_light_rack` | `grid`: 169 state characters, row-major — the whole rack in one call |
521+
| `esphome.winerack1led_light_row` | `row`: `A`-`M`, `states`: 13 characters |
522+
| `esphome.winerack1led_light_bin` | `bin_id`: `A7`, `r`/`g`/`b`, `seconds` to hold it |
523+
| `esphome.winerack1led_clear_all` | nothing |
524+
525+
A state character per bin — `R` ready, `U` urgent, `P` past, `A` aging, `N` no window, `.` empty —
526+
means the whole rack fits in one 169-character string, which is both a single action call and
527+
small enough to be an ordinary sensor state. The package builds that string from
528+
`/api/cellartracker/inventory?view=compact` with the same rule
529+
[`_drink_window_counts`](custom_components/cellar_tracker/cellar_data.py) and the dashboard use,
530+
and pushes it when the cellar syncs rather than on a timer.
531+
532+
### Before you build it
533+
534+
Three things are worth knowing before you cut LED strip:
535+
536+
- **The endpoint is authenticated**, so Home Assistant needs a long-lived access token to read its
537+
own API. The package expects it in `secrets.yaml`.
538+
- **Thirteen strands is more than a classic ESP32 has timing hardware for** — eight RMT channels
539+
and two I2S buses is ten. That bounds how many strands can transmit at once, not how many you
540+
can have, so the config drives them with FastLED, which takes a channel per strand and hands it
541+
back for the next one. `neopixelbus` cannot: it holds a channel per strand for the life of the
542+
node and runs out at ten.
543+
- **Power.** 1014 LEDs at the palette in the file is a few amps, and considerably more if you
544+
brighten it. The file shows the arithmetic.
545+
546+
---
547+
477548
## Troubleshooting and FAQ
478549

479550
### "Invalid username or password" when adding the integration

examples/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Examples
2+
3+
Working configurations that sit on top of the integration. Nothing here is installed by HACS or
4+
loaded by Home Assistant on its own — copy what you want.
5+
6+
| File | What it is |
7+
|---|---|
8+
| [`esphome/winerack1led.yaml`](esphome/winerack1led.yaml) | An ESPHome node that lights a 13 × 13 wine rack with WS2812 strips, one strand per row |
9+
| [`home_assistant/wine_rack_leds.yaml`](home_assistant/wine_rack_leds.yaml) | The Home Assistant package that turns the cellar's inventory into what that node paints |
10+
11+
The two are halves of one thing: the node owns the pixels and knows nothing about wine, and the
12+
package owns the wine and knows nothing about pixels. Each file's header says what you have to set
13+
before it will work; [Lighting the rack](../README.md#lighting-the-rack) in the main README covers
14+
what it does and what to know before building the hardware.

0 commit comments

Comments
 (0)