Skip to content

Commit e3c980e

Browse files
authored
Merge pull request #18 from GuvHas/enhancements/architecture-and-polish
Architecture review follow-up: close every P0, P1 and P2 finding
2 parents 8b27e39 + b506136 commit e3c980e

32 files changed

Lines changed: 2476 additions & 83 deletions

README.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,21 @@ and error semantics; its own `requests`-based transport sets no timeout and is n
6161

6262
## What gets created
6363

64-
Adding the integration creates **one device per account** with **three entities**. It does not
64+
Adding the integration creates **one device per account** with **five entities**. It does not
6565
create an entity per bottle — see [Bottle-level data](#bottle-level-data) for why, and for how to
6666
reach that data.
6767

6868
| Entity | Example | Unit | Device class | State class |
6969
|---|---|---|---|---|
7070
| Total bottles | `142` | `bottles` || `measurement` |
7171
| Total value | `9812.50` | your chosen currency | `monetary` | `total` |
72-
| Status | `Connected` ||| diagnostic |
72+
| Ready to drink | `37` | `bottles` || `measurement` |
73+
| Past drinking window | `4` | `bottles` || `measurement` |
74+
| Last synchronised | `2026-08-28 09:30:00` || `timestamp` | diagnostic |
75+
76+
Upgrading from 0.0.17 or earlier: the diagnostic entity that reported `Connected` now reports
77+
when the cellar last synchronised. It keeps its entity ID, so nothing has to be repointed. Its old
78+
value never changed once the integration was running, so nothing could have been triggering on it.
7379

7480
### Entity IDs
7581

@@ -78,9 +84,15 @@ The device is named after the account, so entity IDs follow the account name:
7884
```
7985
sensor.<account>_total_bottles
8086
sensor.<account>_total_value
81-
sensor.<account>_status
87+
sensor.<account>_ready_to_drink
88+
sensor.<account>_past_drinking_window
89+
sensor.<account>_last_synchronised
8290
```
8391

92+
That third ID is what a **fresh install** gets. An install that predates 0.0.18 keeps
93+
`sensor.<account>_status`, because Home Assistant assigns an entity ID once, at first
94+
registration, and never rewrites it. Both point at the same entity; only the name differs.
95+
8496
**If you installed before v0.0.15**, your entity IDs were generated when the entities were first
8597
registered and Home Assistant keeps them — they will still be `sensor.cellartracker_total_bottles`
8698
and friends. Existing dashboards and automations keep working; only the display names change.
@@ -95,7 +107,8 @@ Per-bottle detail is exposed through an authenticated REST endpoint rather than
95107
state attributes:
96108

97109
```
98-
GET /api/cellartracker/inventory # every bottle, as JSON
110+
GET /api/cellartracker/inventory # every bottle, as JSON
111+
GET /api/cellartracker/inventory?view=compact # the same bottles, nine columns
99112
GET /api/cellartracker/settings # the configured currency and its symbol
100113
```
101114

@@ -135,8 +148,9 @@ The response contains all 66 columns CellarTracker returns; the table above is t
135148

136149
## Installation via HACS
137150

138-
**Requires Home Assistant 2024.7 or newer** — that is the release that added the static-path API
139-
the integration uses to serve its dashboard page.
151+
**Requires Home Assistant 2024.11 or newer** — 2024.7 added the static-path API the integration
152+
uses to serve its dashboard page, and 2024.11 added the `config_entry` argument its data
153+
coordinator now passes.
140154

141155
1. Open **HACS** in Home Assistant.
142156
2. Click the **** menu (top right) → **Custom repositories**.

custom_components/cellar_tracker/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
6767
coordinator = WineCellarData(hass, entry)
6868
await coordinator.async_config_entry_first_refresh()
6969

70-
# Holds coordinators keyed by entry id and nothing else: the views rely on
71-
# every value here being a coordinator.
72-
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
70+
# On the entry rather than in hass.data: it is typed, it dies with the
71+
# entry, and nothing has to remember to clean it up. The views find it
72+
# through hass.config_entries, since they are registered once for the
73+
# component and never hold an entry of their own.
74+
entry.runtime_data = coordinator
7375

7476
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
7577
entry.async_on_unload(entry.add_update_listener(update_listener))
@@ -80,9 +82,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
8082
"""Unload a config entry."""
8183
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
8284
if unload_ok:
83-
# Defaulted: a setup that failed before storing its coordinator still
84-
# gets unloaded, and that must not become a KeyError.
85-
hass.data.get(DOMAIN, {}).pop(entry.entry_id, None)
85+
# Cleared so the views stop seeing this entry immediately, and so a
86+
# setup that failed before assigning it unloads without a KeyError.
87+
entry.runtime_data = None
8688
return unload_ok
8789

8890
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:

0 commit comments

Comments
 (0)