feat(amazon): expose item names and product image per in-transit order - #1362
feat(amazon): expose item names and product image per in-transit order#1362paul43210 wants to merge 1 commit into
Conversation
Shipping emails already contain the full item names (plain-text item
lines, with the truncated quoted subject as fallback) and a product
thumbnail URL (text/html part). Extract them into a new
order_details attribute on the Amazon Packages sensor, keyed by order
number: {order_id: {"name": ..., "image": ...}}.
- utils/amazon.py: get_html_body + extract_amazon_order_details
- shippers/amazon.py: collect details per shipped order during
processing; new "details" parse mode returns them for the same
undelivered-order set as the order attribute
- sensor.py: expose as order_details next to the existing order attr
- Fixture (sanitized real email incl. product image) + utils and
end-to-end shipper tests
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M4JNtf2gPw1jGNjtFy16HF
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #1362 +/- ##
==========================================
+ Coverage 95.07% 99.85% +4.78%
==========================================
Files 8 25 +17
Lines 1482 4223 +2741
==========================================
+ Hits 1409 4217 +2808
+ Misses 73 6 -67
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
This comment was marked as low quality.
This comment was marked as low quality.
firstof9
left a comment
There was a problem hiding this comment.
Please get tests to cover all patch changes.
Thanks.
secondof9
left a comment
There was a problem hiding this comment.
📋 Review Summary
Tip
Review Status: 🟢 APPROVED
Change Type: 🛠️ Refactor (feat)
Review Effort: 🟢 Low
Core Impact: Adds order_details (item name + product image URL) to Amazon in-transit order attributes, surfaced from shipping emails' plain-text item lines and HTML thumbnail — no new sensors, no network calls, fully backward-compatible.
🚦 CI & Pipeline Health Summary
| Check / Workflow Name | Status | Impact on Review |
|---|---|---|
| coverage | ✅ PASSED | Clean — 708 tests, 99.86% coverage |
| main | ✅ PASSED | Baseline build ok |
| Pre-commit checks | ✅ PASSED | ruff + format + pre-commit clean |
| Python 3.14 | ✅ PASSED | No Python 3.14 regressions |
| Validate | ✅ PASSED | Validation passes |
| Autolabel PR | ⏩ SKIPPED | Not applicable |
Note
CI Pipeline Clear: All GitHub Actions workflows completed successfully.
🔍 Architectural Walkthrough
utils/amazon.py — New: `get_html_body()` + `extract_amazon_order_details()`
path/to/utils/amazon.pyget_html_body(msg)(line 152) — Extracts thetext/htmlbody part viamsg.walk()with graceful error handling (ValueError,TypeError,AttributeErrorcaught, logged as DEBUG). Returns empty string on failure — safe fallback. Good defensive practice.AMAZON_PRODUCT_IMG_REGEX(line 160) — Matchesm.media-amazon.com/images/I/andimages-amazon.com/images/I/URLs. Restrictive pattern; won't match arbitrary image URLs. Safe.AMAZON_ITEM_LINE_REGEX(line 162) —^\* (.+)$withre.MULTILINE. Matches Amazon's* item nameformat. Good.AMAZON_SHIPPED_SUBJECT_REGEX(line 164) — Pattern"(.+?)"(with unicode smart quotes). Used as fallback for truncated subject line. Good fallback logic.extract_amazon_order_details(subject, body, msg)(line 169) — Returnsdict[str, str] | None. Prefers plain-text item lines, falls back to subject. Only returns details when at least one signal is present (defensive). No network calls, no downloading. Fully safe.
shippers/amazon.py — Two-pass email parsing with details aggregation
path/to/shippers/amazon.pyprocess()(line 118) — Now calls_parse_amazon_emails()twice (parallel, same email set): once for"order", once for"details". This is the core of the new feature. The details result is keyed by order ID and returned alongside the existing order list._parse_amazon_emails()(line 219) — New"order_details"key initialized to{}in context.detailsbranch (line 249) returns anorder_id -> detailsmapping. Clean separation._process_amazon_email()(line 287) — Passesmsgthrough to_handle_shipping_email()— this is the key change that enables HTML body extraction. Good threading of the message object._handle_shipping_email()(line 319) — Newmsgparameter.extract_amazon_order_details()is called here withmsgso it can access the HTML part. Defensive: only processes details ifmsgis provided. Good.- Tuple unpacking cleanup (lines 427, 604, 648, 685) —
(server_response, data) = await email_search(...)refactored toserver_response, data = await email_search(...). This is a code-style improvement but functionally equivalent. Minor.
sensor.py — Exposing order_details as sensor attribute
path/to/sensor.py_add_amazon_order_attributes()(line 179) — New helper that attachesAMAZON_ORDER_DETAILSto sensor attributes alongsideAMAZON_ORDER. Only set whendetailsis non-empty. Backward-compatible: sensors without details are unchanged._add_amazon_attributes()(line 195) — Calls_add_amazon_order_attributeswhen an order is present. Clean integration.
const.py — New constants
path/to/const.pyATTR_ORDER_DETAILS = "order_details"(line 30) — Attribute name for sensor.AMAZON_ORDER_DETAILS = "amazon_order_details"(line 196) — Shipper result key. Consistent naming convention.
tests/ — Comprehensive new test coverage
path/to/tests/utils/test_amazon.py(line 484) — Unit tests forextract_amazon_order_details: main path (name + image), subject fallback, nothing-extractable guard. All three cases covered.path/to/tests/shippers/test_amazon.py(line 1352) — Integration test viaAmazonShipper.process(): validates that order details are keyed by order number, name extraction works, image URL starts with expected prefix. Uses real sanitized EML fixture.path/to/tests/test_emails/amazon_shipped_details.eml— Real sanitized shipping email with both plain-text item lines and HTML thumbnail. Good test fixture.
🚨 Critical / ⚠️ Warning Issues
No critical issues found. The code is clean, defensive, and well-tested. All CI checks pass. The implementation correctly follows HA core patterns: no blocking I/O, no new config options, no network calls, proper type hints, and clean separation of concerns.
|
This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Why
The Amazon Packages sensor tells you how many parcels are in transit and their order numbers, but not what is coming. The shipping emails already carry both pieces — the plain-text part lists the full item names, and the
text/htmlpart carries a product thumbnail — so the information is present and simply discarded during parsing. This surfaces it as a neworder_detailsattribute so a dashboard can show item names and images instead of bare order numbers.Design
utils/amazon.py:get_html_body()(mirrors the existingget_email_body()for the HTML part) andextract_amazon_order_details(subject, body, msg), which returns{"name": ..., "image": ...}orNone.Shipped: "Item name,...") is used as a fallback only, since it is truncated.m.media-amazon.com/images/I/. Nothing is downloaded — the URL is passed through as-is.Nonewhen neither is extractable, so non-shipping emails add nothing.shippers/amazon.py: details are collected per shipped order during the existing pass. A new"details"parse mode returns them for exactly the same undelivered-order set theorderattribute already uses, so the two attributes cannot disagree.sensor.py: exposed asorder_detailsnext to the existingorderattribute, keyed by order number:{order_id: {"name": ..., "image": ...}}. Only set when non-empty, so sensors without details are unchanged.Tests
amazon_shipped_details.eml), including the HTML part with the product image.extract_amazon_order_details: body extraction, the truncated-subject fallback, and a non-match guard returningNone.AmazonShipper.process()with only IMAP mocked, asserting the details map is keyed by order number and that an order arriving later is still not counted as arriving today.Full suite passes (708 tests, coverage 99.86%), ruff check + format clean. Rebased on current
devas of today.Running live on my instance since 2026-07-20 — names and images render correctly on a real dashboard.
Happy to adjust: the image could be omitted entirely if you'd rather not carry a third-party URL in an attribute, and the details map could be limited to
amazon_packagesonly if exposing it more widely is a concern.🤖 Generated with Claude Code