| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
💬 Questions or feedback? Join the discussion on the Home Assistant community.
A custom Home Assistant integration that tracks your PostNL shipments and announced MyMail letters.
Based upon the original work by @arjenbos.
| Field | Description |
|---|---|
| The email address of your PostNL account. | |
| Password | The password for that account. Stored in the HA config entry and refreshed automatically when the integration triggers a re-authentication. |
Click Configure on the integration card. The form is split into two sections:
| Option | Description |
|---|---|
| Filter by | Days keeps delivered parcels visible for the last N days. Number of parcels keeps only the N most recent regardless of age. |
| Amount | The N used by the filter above. |
| Option | Description |
|---|---|
| Include status history | Adds a history attribute to each parcel — the ordered list of status updates (timestamp, canonical status, original PostNL text), capped to the most recent 20. Off by default. The attribute is kept out of the recorder database. |
Polling isn't a setting here — instead of checking PostNL at the same rate around the clock, the integration adjusts its own cadence to what your parcels are actually doing:
Delivered parcels never affect the cadence — only what's still in transit counts.
Installs that were still on a fixed interval move over automatically — nothing to change. This is now the polling behaviour across the parcel-integrations suite, where the cadence is no longer a setting anywhere.
Standard HA removal applies: Settings → Devices & Services → PostNL → ⋮ → Delete. No PostNL-side cleanup is needed; deleting the config entry stops the polling. To revoke API access entirely, change your PostNL account password — the integration will trigger a re-auth notification, which you can then ignore.
The integration creates one device per PostNL account, named PostNL (<your-email>). With multiple accounts each gets its own device named after its email. The entities below show the friendly-name pattern; their entity_ids carry the same account suffix:
| Friendly name pattern | Description |
|---|---|
| PostNL (account) Incoming parcels | Number of active incoming parcels |
| PostNL (account) Parcel <barcode> | Canonical status of a single incoming shipment |
| PostNL (account) Next delivery | Earliest expected delivery datetime |
| PostNL (account) En route to PostNL Point | Active incoming parcels destined for a PostNL Point pickup location |
| PostNL (account) Delivered parcels | Recently delivered incoming parcels (configurable window) |
| PostNL (account) Outgoing parcels | Number of active outgoing parcels |
| PostNL (account) Outgoing delivered parcels | Recently delivered outgoing parcels (same configurable window) |
| PostNL (account) Letters | Letters announced by PostNL's MyMail service over the last ~2 weeks; unread count and letters list on attributes |
| PostNL (account) Letter <title> (image entity) | Scanned photo of a single announced letter, fetched with your token and served through Home Assistant. Attributes mirror the sensor's letter dict: id, title, date, unread |
A PostNL (account) Deliveries calendar entity is also created, showing expected delivery dates for active incoming parcels — read-only, no extra API calls, letters are not included.
A PostNL (account) Refresh button entity forces an immediate poll, without waiting for the next scheduled interval.
Every parcel exposed on a sensor attribute uses a carrier-agnostic shape:
| Key | Type | Meaning |
|---|---|---|
| carrier | string | "PostNL" |
| barcode | string | Parcel tracking number |
| sender | string | null | Sender name (e.g. webshop) |
| receiver | string | null | Recipient name |
| status | ParcelStatus | Canonical status — see the status reference |
| raw_status | string | null | Original PostNL status description (a Dutch human-readable string) |
| delivered | bool | Whether the parcel has been delivered |
| delivered_at | ISO 8601 | null | Delivery moment, if known |
| planned_from | ISO 8601 | null | Expected delivery window start |
| planned_to | ISO 8601 | null | Expected delivery window end |
| pickup | bool | Destined for a PostNL Point rather than a home address |
| pickup_point | string | null | PostNL Point name when pickup is true (always null for now — PostNL does not expose the field) |
| url | string | null | Deep link to the parcel's tracking page on jouw.postnl.nl |
| weight | float | null | Parcel weight in kilograms. May be null for delivered parcels or when PostNL has not yet attached the data. |
| dimensions | dict | null | Parcel dimensions in centimeters: {length, width, height, text} where text is a pre-formatted "L x W x H cm" string. Same coverage as weight. |
| history | list | null | Ordered status timeline (oldest → newest), each entry {timestamp, status, raw_status}, capped to the most recent 20. null unless the Parcel history option is enabled — see Options. |
| raw | dict | The original PostNL payload |
Each announced letter is exposed as an image entity so the scan loads directly in dashboards and notifications. See examples/automations/notify_when_letter_arrives.yaml for a ready-to-paste notification.
status on every parcel is one of the canonical ParcelStatus values below. Use these in your automations rather than PostNL's raw Dutch description — the raw value stays available on raw_status for power users.
| status | Meaning | PostNL signal that maps here |
|---|---|---|
| registered | PostNL knows about the label but the parcel is not yet in transit | statusPhase.message containing "aangemeld" or "verwacht" |
| in_transit | Picked up; somewhere in PostNL's network | statusPhase.message containing "onderweg", "ontvangen" or "gesorteerd" |
| out_for_delivery | On the delivery vehicle today | statusPhase.message containing "wordt vandaag bezorgd", "onderweg naar het bezorgadres" or "onderweg naar de bezorger" |
| at_pickup_point | Arrived at the chosen PostNL Point, ready to be collected | statusPhase.message containing "ligt klaar bij postnl punt" or similar |
| delivered | Handed over (mailbox, recipient, neighbour, picked up) | shipment.delivered == true (authoritative); fallback statusPhase.message containing "bezorgd" |
| returning | Failed delivery, on the way back to the sender | statusPhase.message containing "retour" or "teruggestuurd" |
| unknown | Raw description we have not mapped yet | anything else — logged once at warning level with a ready-to-paste issue link so it can be added to the map |
Because PostNL's statusPhase.message is a human-readable Dutch string (not a stable API enum), the mapping uses ordered substring matching — so minor wording variants still resolve correctly. If you see an unknown for a status the integration ought to recognise, open an issue with the raw value (visible in the integration debug logs and on the parcel sensor under raw_status).
The coordinator fires events on the HA event bus when something interesting happens to a parcel, so automations can react without polling per-parcel sensors.
| Event | When | Payload |
|---|---|---|
| postnl_parcel_registered | A new barcode appears in the active list | The full parcel dict (see the table above) |
| postnl_parcel_status_changed | A known barcode's status value changes, except the final hop to delivered | Same payload plus old_status and new_status |
| postnl_parcel_delivered | An incoming parcel is delivered | The full parcel dict |
| postnl_parcel_delivery_time_changed | A known barcode's expected delivery time changes to a new value | Same payload plus old_planned_from, new_planned_from, old_planned_to, new_planned_to |
| postnl_outgoing_parcel_status_changed | A known outgoing parcel (something you sent, or a return) changes status, except the final hop to delivered | Same payload plus old_status and new_status |
| postnl_outgoing_parcel_delivered | An outgoing parcel reaches the recipient | The full parcel dict |
| postnl_letter_announced | A new letter appears in the MyMail feed | The letter dict (id, title, date, unread, image_url) plus carrier: "PostNL" |
Every payload also carries a device_id identifying the PostNL account the parcel or letter belongs to, so automations can tell two accounts apart.
Events do not fire for parcels or letters that were already in your account when HA first started.
If you build automations in the UI, these same events are also available as no-code device triggers (Settings → Automations → Create → Add trigger → Device), scoped to the selected account's device. The raw events above are there for templates and YAML automations.
See examples/automations/ for ready-to-paste event-driven automations, or the parcel aggregator for a carrier-agnostic re-emit layer that fires parcel_aggregator_parcel_* events covering every installed carrier in one go.
Ready-to-paste automations and dashboard cards live in examples/.
Third-party cards that work with this integration's sensors:
To capture verbose information about the PostNL API responses (useful when reporting a bug or helping map a new status value), enable debug logging for the integration:
logger:
default: warning
logs:
custom_components.postnl: debug| Symptom | Likely cause |
|---|---|
| invalid_auth error during setup | Wrong email or password |
| cannot_connect error during setup | PostNL API is unreachable; check your network |
| Re-authentication prompt appears | PostNL session expired and could not be refreshed silently; log in again |
| Sensors disappear after delivery | Expected — delivered parcels move to the delivered sensor (visible window controlled by the options filter) |
| Sensors not updating | Check Settings → System → Logs for postnl entries |
This integration is part of ha-parcel-integrations — a family of parcel-carrier integrations that all publish the same canonical parcel format, statuses and events.
This is an independent, community-built project. It is not affiliated with, endorsed by, sponsored by, or supported by PostNL, Home Assistant, or any other third party referenced in this project. Please don't contact PostNL for support with this integration.
All third-party trademarks, trade names, product names, logos, and other brand assets are the property of their respective owners. References to them are solely to identify the relevant carrier or service and do not imply affiliation, sponsorship, or endorsement. Nothing in this project grants or implies any licence or right to use third-party brand assets.
This integration may rely on public, unofficial, or undocumented carrier interfaces, accessed with your own account or API key where required. These may change or be withdrawn without notice and may be subject to PostNL's terms. Data is sent only to PostNL's own services or those of its group; this project operates no servers of its own. You are responsible for ensuring that your use complies with applicable law and those terms. Use is at your own risk; see the licence for warranty limitations.
This fork is maintained by @peternijssen. The original integration is by @arjenbos — fixes that apply to both forks are filed as PRs against the upstream arjenbos/ha-postnl.
Pull requests and issues are welcome. Please open an issue before submitting a large change.
MIT
| Back | FazBrowse Home | New Git URL |