Files
ha-freshharvest/README.md
T
flan a97f0aec6a Add the Fresh Harvest dashboard view as an example
Ships the tab that is running on ha-box: a delivery countdown, order tiles,
box contents rendered from the item attributes, and the still-open order.
2026-08-03 19:02:11 +00:00

79 lines
3.3 KiB
Markdown

# ha-freshharvest
Unofficial Home Assistant integration for [Fresh Harvest](https://freshharvest.com/),
the Georgia local-produce delivery subscription.
## Entities
| Entity | Example | Notes |
| --- | --- | --- |
| `sensor.fresh_harvest_next_delivery` | `2026-08-04` | Attributes: `delivery_day`, `box` |
| `sensor.fresh_harvest_next_delivery_total` | `109.06` | Attributes: `subtotal`, `tax`, `delivery_fee` |
| `sensor.fresh_harvest_next_delivery_items` | `14` | Attributes: `box`, `produce`, `add_ons` |
| `sensor.fresh_harvest_open_order_delivery` | `2026-08-11` | The order you can still change |
| `sensor.fresh_harvest_shopping_window` | `Shop tomorrow` | `closed` when nothing is customizable |
The next delivery and the *open* order are usually two different deliveries.
Once an order passes its cutoff it locks for packing, and the cart you can still
edit is the following week's.
## How it works
Fresh Harvest is not on Shopify, Farmigo, or Local Line — the page metadata
reports `Vy Technology - Custom Code`. It is a server-rendered jQuery site with
no JSON API and no mobile app, so this integration signs in and parses HTML.
Login is a two-step handshake:
1. `GET /s/popup/login` returns the form plus two hidden anti-replay fields,
`LoginSecurity` and `SubmitToken`, minted per session.
2. `POST /s/submit/login` with `LoginEmail`, `LoginPassword`, both tokens, and
an empty `Redirect`, yielding an `fh_session_authenticated` cookie.
The tokens are bound to the cookie issued by step 1, so both requests must
share a cookie jar. Everything the integration needs then comes from a single
`GET /p/dashboard/details`, which carries the delivery day, next arrival date,
both upcoming carts, their contents, and their totals.
## Markup notes
Two traps are worth recording, since neither is guessable from the outside:
- **HTTP status means nothing.** Every `/p/*` path returns 200, including
invented ones. Signed-in state is detected by the presence of a Sign Out
control, not by a status code.
- **`cart-contents-skipped` does not mean the order was skipped.** It marks the
locked cart — the one past its cutoff and arriving next. Treating it as
"skipped" reports the wrong delivery as cancelled. The reliable signal for
"can still be changed" is a non-empty `.cart-customize-wrapper`.
## Dashboard
[examples/dashboard-view.yaml](examples/dashboard-view.yaml) is a ready-made tab
for these sensors — a countdown heading ("Arriving tomorrow"), tiles for the
date, total and item count, the full box contents rendered from the attributes,
and the still-changeable order with a link back to the portal. Paste it under
`views:` in the raw configuration editor.
## Installation
Copy `custom_components/freshharvest/` into your Home Assistant
`config/custom_components/` directory and restart Home Assistant, then add the
integration from **Settings → Devices & Services**. Credentials are your normal
freshharvest.com email and password.
## Tests
```
pip install beautifulsoup4 pytest
pytest tests/
```
The fixture is synthetic but mirrors the real markup; the live page carries the
account holder's name, address, and phone number, so it is not committed.
## Disclaimer
Unofficial and unaffiliated. Polls once every 6 hours; please do not lower that
— this is a small business's website, not an API.