💬 Questions or feedback? Join the discussion on the Home Assistant community.
A custom Home Assistant integration that tracks your GLS parcels in Austria, Belgium, Canada, Croatia, the Czech Republic, Denmark, Finland, France, Germany, Hungary, Ireland, Italy, Luxembourg, the Netherlands, Poland, Serbia, Slovakia, Slovenia, Switzerland and the United States. No GLS account is needed — you enter the tracking number and delivery postal code yourself.
- Features
- Requirements
- Installation
- Configuration
- Options
- Removal
- Sensors
- Parcel status reference
- Events
- Examples
- Debugging
- Troubleshooting
- Related integrations
- Disclaimer
- Contributing
- License
- Track multiple GLS parcels by tracking number — no account
- Add parcels from the options, a service, or a dashboard button
- Per-parcel sensor per tracked shipment, with full status details as attributes
- Incoming, next-delivery, en-route and awaiting-pickup summary sensors
- Delivered-parcels sensor and an optional per-parcel status history timeline
- Weight and dimensions where GLS provides them
- Automatic lifecycle management — per-parcel sensors appear and disappear as parcels move through delivery
- A GLS parcel delivered to a supported country. Austria, Belgium, Canada, Croatia, the Czech Republic, Denmark, Finland, France, Germany, Hungary, Ireland, Italy, Luxembourg, the Netherlands, Poland, Serbia, Slovakia, Slovenia, Switzerland and the United States are available today; the setup form links to the organisation discussion for requesting another country
- Open HACS → Integrations → ⋮ → Custom repositories
- Add this repository URL and select category Integration
- Search for GLS and install it
- Restart Home Assistant
- Copy the
glsfolder into yourconfig/custom_components/directory - Restart Home Assistant
- Go to Settings → Devices & Services → Add Integration
- Search for GLS
- Pick your country and enter your delivery postal code (the one parcels usually go to)
- Click Submit
That's it — setup only asks for the country and postal code. The postcode becomes the default for parcels you add later, so adding a parcel usually only needs its number.
For Canada, the postal code is used for GLS Canada's enhanced tracking
response. The carrier's complete native response is available in the parcel
sensor's unrecorded raw attribute.
For the United States and Poland, GLS tracks on the number alone, so the postal code is only the hub's label there — setup still asks for it so every GLS hub works the same way. Both report status, delivery time and scan history, and neither publishes weight or dimensions. Polish parcels are tracked through GLS Poland's own national system, which also reports a parcel's arrival at a GLS Point separately from you collecting it.
You can add multiple hubs — one per delivery postal code (e.g. home and work). Each hub is its own GLS (postcode) device with its own parcels.
A GLS hub holds your tracked parcels. Add them any of three ways — new per-parcel sensors appear immediately, no restart or manual refresh needed:
- Options — integration card → Configure → Parcels → edit the list of tracking codes (add or remove any number, then save).
- Service — call
gls.track_parcelwith atracking_code(and optionalpostal_code, which picks the hub when you run several).gls.untrack_parcelremoves one. - Dashboard — a text field + button that calls the service. See
examples/dashboards/add_parcel_card.yaml.
You can use either identifier GLS gives out: the long parcel number
(e.g. 13290054100304) or the short tracking ID (e.g. 00L1B3BX). Find
them in the GLS track & trace mail/SMS or on gls-info.nl.
Click Configure on the integration card — a menu with two pages:
| Page | Description |
|---|---|
| Parcels | Your tracked codes as one editable list — add or remove any number, then save. |
| Settings | Delivered-parcel retention and status history, described below. |
| Setting (on the Settings page) | Description |
|---|---|
| Delivered parcels: filter by / amount | Keep delivered parcels in the delivered sensor for the last N days, or keep only the N most recent (parcels). Default: 7 days. Parcels stay tracked — this only controls the sensor. |
| Include status history | Add a per-parcel status history attribute. Off by default. |
Polling isn't a setting here — the integration adjusts its own cadence to what your tracked parcels are actually doing:
- Quiet hours — no polling between 00:00–06:00 local time, aside from one catch-up check at each end of that window (around midnight and around 6 AM), so an overnight update is never missed.
- Hot (every 15 minutes) — while any tracked parcel is out for delivery today, starting an hour before its delivery window opens (or immediately if no window is known yet).
- Normal (every 45 minutes) — for anything else still on its way.
- Fully paused — once every tracked parcel has been delivered, or nothing is tracked at all, polling stops until you add a parcel back (adding one always triggers an immediate check, regardless of the pause).
- A small, fixed per-hub offset is added on top, so not every GLS hub out there polls at exactly the same second.
Standard HA removal applies: Settings → Devices & Services → GLS → ⋮ → Delete. Nothing is stored on GLS' side.
Each hub is a GLS (postcode) device. The entities below show the friendly-name pattern (with multiple hubs each carries its own postcode):
| Friendly name | Description |
|---|---|
GLS (postcode) Incoming parcels |
Number of active (not-yet-delivered) tracked parcels |
GLS (postcode) Parcel <number> |
Canonical status of a single tracked parcel |
GLS (postcode) Next delivery |
Earliest expected delivery datetime |
GLS (postcode) En route to ParcelShop |
Active parcels still in transit to a GLS ParcelShop |
GLS (postcode) Awaiting pickup |
Parcels that have arrived at a ParcelShop and are ready to collect |
GLS (postcode) Delivered parcels |
Recently delivered tracked parcels (retention configurable) |
GLS (postcode) Last successful update |
Diagnostic timestamp of the last successful poll |
A GLS (postcode) Deliveries calendar entity is also created, showing
expected delivery dates for active parcels — read-only, no extra API calls.
A GLS (postcode) Refresh button entity forces an immediate poll, without
waiting for the next scheduled interval.
Not every field is populated by every country — weight/dimensions/
sender/receiver/pickup_point and the delivery window vary by which
data GLS' own backend for that country exposes. Every parcel exposed on a
sensor attribute uses the same carrier-agnostic shape regardless:
| Key | Type | Meaning |
|---|---|---|
carrier |
string | "GLS" |
barcode |
string | Parcel tracking number |
sender |
string | null | Sender name |
receiver |
string | null | Recipient name |
status |
ParcelStatus |
Canonical status — see the status reference |
raw_status |
string | null | Original GLS status description |
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 ParcelShop rather than a home address |
pickup_point |
string | null | ParcelShop name when pickup is true |
url |
string | null | Deep link to the parcel's tracking page |
weight |
float | null | Parcel weight in kilograms |
dimensions |
dict | null | {length, width, height, text} in centimeters |
history |
list | null | Ordered status timeline (oldest → newest), each {timestamp, status, raw_status}. null unless the status history option is enabled — see Options. |
raw |
dict | The original GLS API payload |
status on every parcel is one of the canonical ParcelStatus values
below — use these in automations rather than GLS' raw Dutch strings.
status |
Meaning | GLS state |
|---|---|---|
registered |
GLS was notified of the parcel | 0 (Aangekondigd bij GLS) |
in_transit |
In GLS' network | 1, 2 (ontvangen / op depot) |
out_for_delivery |
On the delivery vehicle today | 3 (Onderweg - geladen voor aflevering) |
at_pickup_point |
Arrived at a ParcelShop, ready to collect | (mapped once observed) |
delivered |
Handed over | 4 (Afgeleverd) |
returning |
On the way back to the sender | (mapped once observed) |
problem |
Carrier reports an exception | (mapped once observed) |
unknown |
A state we have not mapped yet | anything else — logged once at warning level with a ready-to-paste issue link |
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 |
|---|---|---|
gls_parcel_registered |
A new parcel appears in the active list | The full parcel dict (see the table above) |
gls_parcel_status_changed |
A parcel's canonical status value changes, except the final hop to delivered |
Same payload plus old_status and new_status |
gls_parcel_delivered |
An incoming parcel is delivered | The full parcel dict |
gls_parcel_delivery_time_changed |
A parcel's expected delivery time changes to a new value | Same payload plus old_planned_from, new_planned_from, old_planned_to, new_planned_to |
Every payload also carries a device_id. Events do not fire for parcels
that were already tracked 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).
See examples/automations/ for ready-to-paste
event-driven automations.
| Service | Description |
|---|---|
gls.track_parcel |
Start tracking a parcel — tracking_code (required) and postal_code (optional, defaults to the hub postal code). |
gls.untrack_parcel |
Stop tracking a parcel — tracking_code. |
Ready-to-paste automations and dashboard snippets live in
examples/, including a card that adds a parcel from a
dashboard.
Third-party cards that work with this integration's sensors:
To capture the raw GLS API response, enable debug logging:
logger:
default: warning
logs:
custom_components.gls: debugRestart Home Assistant, wait for the next poll (or press the Refresh button), and check Settings → System → Logs.
| Symptom | Likely cause |
|---|---|
cannot_connect during setup |
GLS is unreachable; check your network |
A parcel shows unknown |
GLS has not scanned it yet, or its state is not mapped — check the logs for a ready-to-paste issue link |
| Sensors not updating | Check Settings → System → Logs for gls 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.
- Parcel Aggregator rolls every installed carrier up into one set of sensors.
- Browse the organisation for the current list of supported carriers.
This is an independent, community-built project. It is not affiliated with, endorsed by, sponsored by, or supported by GLS, Home Assistant, or any other third party referenced in this project. Please don't contact GLS 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 GLS's terms. Data is sent only to GLS'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.
Pull requests and issues are welcome. Please open an issue before submitting a large change.
MIT