Hey one question how did you got top header working with postnl and dhl icon with both 0.
i see two red ! and - next to it

In got next code as header;
type: heading
heading: Pakketten
heading_style: title
icon: mdi:package-variant-closed
badges:
- type: entity
show_state: true
show_icon: true
entity: sensor.postnl_delivery
name: PostNL
icon: phu:postnl
- type: entity
show_state: true
show_icon: true
entity: sensor.aantal_aankomende_dhl_pakketjes
icon: phu:dhl
i got it i think the only thing is how do i remove packages and pakketjes.
![]()
add new template to you configuration.yaml
template:
- sensor:
- name: "PostNL Pakketten Aantal"
state: "{{ states('sensor.postnl_delivery').split(' ')[0] }}"
unit_of_measurement: "" # Dit haalt de 'packages' of 'aantal pakketten' weg.
- name: "DHL Pakketten Aantal"
state: "{{ states('sensor.aantal_aankomende_dhl_pakketjes').split(' ')[0] }}"
unit_of_measurement: "" # Dit haalt de 'packages' of 'aantal pakketten' weg.
Then the code for in the header would be:
type: heading
heading: Pakketten
heading_style: subtitle
icon: mdi:package-variant-closed
badges:
- type: entity
show_state: true
show_icon: true
entity: sensor.postnl_pakketten_aantal
name: PostNL
icon: phu:postnl
- type: entity
show_state: true
show_icon: true
entity: sensor.dhl_pakketten_aantal
name: DHL
icon: phu:dhl
Hope this helps
no it did not i tried i got erorrs in yaml file in studio. then tried to fix it and it still shows package so the one from dhl code. that i need to add as an helper. i copied it and changed it a little. see here.
{% set dhl_pakketjes = state_attr('sensor.dhl_packages', 'parcels') or [] %}
{% set undelivered_count = 0 %}
{% for pakket in dhl_pakketjes %}
{% set status = pakket.status | lower %}
{% if 'delivered' not in status and 'collected' not in status %}
{% set undelivered_count = undelivered_count + 1 %}
{% endif %}
{% endfor %}
{{ undelivered_count }}
and change it to to
{% set dhl_pakketjes = state_attr('sensor.postnl_delivery', 'parcels') or [] %}
{% set undelivered_count = 0 %}
{% for pakket in dhl_pakketjes %}
{% set status = pakket.status | lower %}
{% if 'delivered' not in status and 'collected' not in status %}
{% set undelivered_count = undelivered_count + 1 %}
{% endif %}
{% endfor %}
{{ undelivered_count }}
it works but and it removed packages teklst but there is still dhl_pakketjes dont know in what to change it. i dont think it will count dhl. because ther is still some dhl_pakketjes.

Donāt understand the correction.
you name a sensor ādhl_pakketjesā and use the sensor.postnl_delivery.
So it doesn count the DHL but the PostNL
Will try to update my github with the yaml files.
Did made for the DHL counting 2 sensors.
This is copy past from sensor.yaml file
# DHL pakketten
- sensor:
- name: "Aantal Aankomende DHL Pakketjes"
state: >
{% set dhl_pakketjes = state_attr('sensor.dhl_packages', 'parcels') or [] %}
{% set undelivered_count = namespace(value=0) %} {# Initialize as a namespace object #}
{% for pakket in dhl_pakketjes %}
{% set status = pakket.status | lower %}
{% if 'delivered' not in status and 'collected' not in status %}
{% set undelivered_count.value = undelivered_count.value + 1 %} {# Update the value property #}
{% endif %}
{% endfor %}
{{ undelivered_count.value }} {# Access the value property #}
#aantal pakketen
- sensor:
- name: "PostNL Pakketten Aantal"
state: "{{ states('sensor.postnl_delivery').split(' ')[0] }}"
unit_of_measurement: "" # Dit haalt de 'packages' of 'aantal pakketten' weg.
- name: "DHL Pakketten Aantal"
state: "{{ states('sensor.aantal_aankomende_dhl_pakketjes').split(' ')[0] }}"
unit_of_measurement: "" # Dit haalt de 'packages' of 'aantal pakketten' weg.
this what is see when i add it missing property platform.
configuration.yaml in studio code server.
i am doing somthing wrong but dont know what
# Loads default set of integrations. Do not remove.
default_config:
# Load frontend themes from the themes folder
frontend:
themes: !include_dir_merge_named themes
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
#washer sensor
template:
- sensor:
- name: "wasmachinenl time"
state: >
{% set ct = states('sensor.wasmachinenl_washer_completion_time') | as_datetime %}
{{ '00:00' if now() > ct else (ct - now()).total_seconds() | timestamp_custom('%H:%M', false) }}
- sensor:
- unique_id: washer_remaining_time
name: "Remaining Time"
state: >-
{% set ct = states('sensor.wasmachinenl_washer_completion_time') | as_datetime %}
{{ '00:00' if now() > ct else (ct - now()).total_seconds() | timestamp_custom('%H:%M', false) }}
- sensor:
- unique_id: drogernl_dryer_completion_time
name: "Remaining Time"
state: >-
{% set rem_h = (as_timestamp(states.sensor.drogernl_dryer_completion_time.state) - as_timestamp(now())) | timestamp_custom('%-H', false) %}
{% set rem_m = (as_timestamp(states.sensor.drogernl_dryer_completion_time.state) - as_timestamp(now())) | timestamp_custom('%-M', false) %}
{% if int(rem_h) > 0.9 %} {{ rem_h }} hour(s) and {{ rem_m }} minute(s) {% else %} {{ rem_m }} minute(s) {% endif %}
#dhl pakketten
command_line:
- sensor:
name: DHL Packages
command:
value_template: "{{ value_json.parcels | list | count }}"
json_attributes:
- parcels
sensor:
#mediarr
- platform: mediarr
seer:
url: localhost
api_key: your_api_key
max_items: 45 #example how many items you want in sensor, you can chosse a different amount in card
tmdb_api_key:
trending: true # Optional
discover: true # Optional
popular_movies: false # Optional, recommended to use tmdb for popular
popular_tv: true # Optional, recommended to use tmdb for popular
plex: # Optional
url: Plex url
token: your_token
max_items: 45 #example how many items you want in sensor, you can chosse a different amount in card
tmdb_api_key: "your_tmdb_api_key"
language: en #default
jellyfin: # Optional
url: jellyfin url
token: your_api_key
max_items: 45 #example how many items you want in sensor, you can chosse a different amount in card
tmdb_api_key: "your_tmdb_api_key"
language: en #default
sonarr: # Optional
url: http://192.168.
api_key:
max_items: 45 #example how many items you want in sensor, you can chosse a different amount in card
days_to_check: 60 #example
sonarr2: # Optional
url: http://192.168.
api_key:
max_items: 45 #example how many items you want in sensor, you can chosse a different amount in card
days_to_check: 60 #example
radarr: # Optional
url: http://localhost:7878
api_key: your_radarr_api_key
max_items: 45 #example how many items you want in sensor, you can chosse a different amount in card
days_to_check: 60 #breaking change
radarr2: # Optional
url: http://localhost:7878
api_key: your_radarr_api_key
max_items: 45 #example how many items you want in sensor, you can chosse a different amount in card
days_to_check: 60 #breaking change
trakt: # Optional
client_id:
client_secret:
tmdb_api_key: # Required for posters
trending_type: shows # Options: movies, shows, both
max_items: 30 #example how many items you want in sensor, you can chosse a different amount in card
tmdb: # Optional
tmdb_api_key:
trending_type: tv # Options: movie, tv, all
max_items: 60 #example how many items you want in sensor, you can choose a different amount in card
trending: true # Default endpoint
now_playing: false # Optional
upcoming: false # Optional
on_air: false # Optional
airing_today: false # Optional
popular_movies: false
popular_tv: true
filters:
min_year: 2024 # Only show content from 2024 onwards
exclude_talk_shows: true
exclude_genres: [10763, 10764, 10767]
# DHL pakketten
- sensor:
- name: "Aantal Aankomende DHL Pakketjes"
state: >
{% set dhl_pakketjes = state_attr('sensor.dhl_packages', 'parcels') or [] %}
{% set undelivered_count = namespace(value=0) %} {# Initialize as a namespace object #}
{% for pakket in dhl_pakketjes %}
{% set status = pakket.status | lower %}
{% if 'delivered' not in status and 'collected' not in status %}
{% set undelivered_count.value = undelivered_count.value + 1 %} {# Update the value property #}
{% endif %}
{% endfor %}
{{ undelivered_count.value }} {# Access the value property #}
#aantal pakketen
- sensor:
- name: "PostNL Pakketten Aantal"
state: "{{ states('sensor.postnl_delivery').split(' ')[0] }}"
unit_of_measurement: "" # Dit haalt de 'packages' of 'aantal pakketten' weg.
- name: "DHL Pakketten Aantal"
state: "{{ states('sensor.aantal_aankomende_dhl_pakketjes').split(' ')[0] }}"
unit_of_measurement: "" # Dit haalt de 'packages' of 'aantal pakketten' weg.
i have tried to remove some key i think i am safe leaking passwords but this what mine configuration.yaml looks like
Configuratiewaarschuwingen
Invalid config for āsensorā at configuration.yaml, line 113: required key āplatformā not provided Invalid config for āsensorā at configuration.yaml, line 126: required key āplatformā not provided.
Aha the sensor i provided and you add to your configuration.yaml are template type.
I donāt use it because I have a separate sensor.yaml file, and donāt add them to configuration.yaml
For you:
Move
# DHL pakketten
- sensor:
- name: "Aant..... Till the end .
To the ātemplateā section of you configuration.yaml
Or type ātemplate:ā above the add code but Donāt know if itās valid to use 2 template sections
Maybe this will help
This is wat I get at the moment:
In configuration.yaml i have the following for retrieving info from DHL:
command_line:
- sensor:
name: DHL Packages
command: 'curl -v --cookie "X-AUTH-TOKEN=heel lang token" https://my.dhlecommerce.nl/receiver-parcel-api/parcels'
value_template: "{{ value_json.parcels | list | count }}"
json_attributes:
- parcels
And the code for the card is as follows:
type: custom:mod-card
style: |
ha-card {
border-radius: 20px !important;
background-color: rgba(10, 10, 10, 0.4) !important;
}
card:
type: vertical-stack
cards:
- type: heading
heading: Pakketten
heading_style: title
badges:
- type: entity
show_state: true
show_icon: true
entity: sensor.postnl_delivery
icon: phu:postnl
- type: entity
show_state: true
show_icon: true
entity: sensor.dhl_packages_onderweg
icon: phu:dhl
card_mod:
style: |
ha-card {
margin-top: 10px !important;
margin-right: 10px !important;
margin-left: 20px !important;
}
- type: custom:auto-entities
card:
type: entities
show_header_toggle: false
card_mod:
style: |
ha-card {
background: none;
margin-top: -25px
}
filter:
template: >
{# DHL ophalen #} {% set dhl_pakketjes =
state_attr('sensor.dhl_packages','parcels') or [] %} {% set
dhl_onderweg = namespace(list=[]) %} {% set dhl_recent_afgeleverd =
namespace(list=[]) %}
{# DHL pakketten #} {% for pakket in dhl_pakketjes %}
{% set status = pakket.status | lower %}
{% set tijd_raw = pakket.receivingTimeIndication.start if pakket.receivingTimeIndication.start is defined else pakket.receivingTimeIndication.moment %}
{% set tijd_end = pakket.receivingTimeIndication.end if pakket.receivingTimeIndication.end is defined else pakket.receivingTimeIndication.moment %}
{% set tijd = 'Onbekend' %}
{% set tijd_ts = 9999999999 %}
{% if tijd_raw %}
{% set tijd_ts = as_timestamp(tijd_raw) %}
{% set tijd = tijd_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% if tijd_raw != tijd_end %}
{% set tijd = tijd_ts | timestamp_custom('%d-%m-%Y %H:%M') ~ ' - ' ~ as_timestamp(tijd_end) | timestamp_custom('%H:%M') %}
{% endif %}
{% endif %}
{% set naam = pakket.sender.name if pakket.sender.name else 'Onbekende afzender' %}
{% set barcode = pakket.barcode %}
{% set nette_status = pakket.status | replace('_', ' ') | lower() | capitalize() %}
{% if nette_status == 'Prenotification received' %}
{% set nette_status = 'Aangemeld' %}
{% endif %}
{% if nette_status == 'Shipment picked up' %}
{% set nette_status = 'Ontvangen door DHL' %}
{% endif %}
{% if nette_status == 'Parcel picked up at parcelshop' %}
{% set nette_status = 'Ontvangen door DHL' %}
{% endif %}
{% if nette_status == 'Shipment acceptance parcelshop' %}
{% set nette_status = 'Ontvangen door DHL' %}
{% endif %}
{% if nette_status == 'Parcel sorted at hub' %}
{% set nette_status = 'Gesorteerd' %}
{% endif %}
{% if nette_status == 'In transit' %}
{% set nette_status = 'In doorvoer' %}
{% endif %}
{% if nette_status == 'Parcel arrived at local depot' %}
{% set nette_status = 'Bij lokale depot' %}
{% endif %}
{% if nette_status == 'Out for delivery' %}
{% set nette_status = 'Onderweg' %}
{% endif %}
{% if nette_status == 'Not home system intervention delivery at parcelshop' %}
{% set nette_status = 'Onderweg naar DHL punt' %}
{% endif %}
{% if nette_status == 'Notification for parcelshop collection has been sent' %}
{% set nette_status = 'Op te halen bij DHL punt' %}
{% endif %}
{% if nette_status == 'Delivered' or nette_status == 'Delivered in mailbox' or nette_status == 'Collected at parcelshop' %}
{% set nette_status = 'Bezorgd' %}
{% set verwacht_of_bezorgd = 'Bezorgd' %}
{% else %}
{% set verwacht_of_bezorgd = 'Verwacht' %}
{% endif %}
{% set dhl_track_url = 'https://www.dhl.com/nl-en/home/tracking.html?tracking-id=' ~ barcode ~ '&submit=1' %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.dhl_packages',
'name': '' ~ naam ~ ' - ' ~ barcode,
'state': nette_status,
'secondary': '' ~ verwacht_of_bezorgd ~': ' ~ tijd,
'icon': 'phu:dhl',
'color': '#ffcc00',
'tap_action': {
'action': 'url',
'url_path': dhl_track_url
},
'sort_tijd': tijd_ts
} %}
{% if 'delivered' in status or 'collected' in status %}
{% if pakket.receivedDaysAgo is not none and pakket.receivedDaysAgo <= 3 %}
{% set dhl_recent_afgeleverd.list = dhl_recent_afgeleverd.list + [row] %}
{% endif %}
{% else %}
{% set dhl_onderweg.list = dhl_onderweg.list + [row] %}
{% endif %}
{% endfor %}
{# PostNL ophalen #} {% set postnl_enroute =
state_attr('sensor.postnl_delivery', 'enroute') or [] %} {% set
postnl_delivered = state_attr('sensor.postnl_delivery', 'delivered')
or [] %} {% set postnl_onderweg = namespace(list=[]) %} {% set
postnl_recent_afgeleverd = namespace(list=[]) %}
{# PostNL onderweg pakketten #} {% for pakket in postnl_enroute %}
{% set naam = pakket.name if pakket.name else 'Onbekende afzender' %}
{% set barcode = pakket.key %}
{% set nette_status = pakket.status_message | default('Status onbekend') %}
{% if 'nog niet door PostNL ontvangen of verwerkt' in nette_status %}
{% set nette_status = 'Aangemeld' %}
{% endif %}
{% if 'is ontvangen door PostNL' in nette_status %}
{% set nette_status = 'Verzonden' %}
{% endif %}
{% if 'is gesorteerd' in nette_status %}
{% set nette_status = 'Gesorteerd' %}
{% endif %}
{% if 'is onderweg' in nette_status %}
{% set nette_status = 'Onderweg' %}
{% endif %}
{% if 'Je hebt betaald' in nette_status %}
{% set nette_status = 'Inklaringskosten betaald' %}
{% endif %}
{% set tijd_raw = pakket.planned_from %}
{% set tijd = 'Onbekend' %}
{% set tijd_ts = 9999999999 %}
{% if tijd_raw %}
{% set tijd_ts = as_timestamp(tijd_raw) %}
{% set tijd = tijd_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% endif %}
{% set postnl_track_url = pakket.url if pakket.url is defined else 'https://www.postnl.nl/tracktrace' %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.postnl_delivery',
'name': '' ~ naam ~ ' - ' ~ barcode,
'state': nette_status,
'secondary': 'Verwacht: ' ~ tijd,
'icon': 'phu:postnl',
'color': '#FB6200',
'tap_action': {
'action': 'url',
'url_path': postnl_track_url
},
'sort_tijd': tijd_ts
} %}
{% set postnl_onderweg.list = postnl_onderweg.list + [row] %}
{% endfor %}
{# PostNL recent afgeleverd pakketten (max 3 dagen) #} {% for pakket
in postnl_delivered %}
{% set tijd_raw = pakket.delivery_date %}
{% set tijd = 'Onbekend' %}
{% set tijd_ts = 0 %}
{% set dagen_verschil = 999 %}
{% if tijd_raw %}
{% set tijd_ts = as_timestamp(tijd_raw) %}
{% set nu = now().timestamp() %}
{% set dagen_verschil = (nu - tijd_ts) / 86400 %}
{% set tijd = tijd_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% endif %}
{% if dagen_verschil <= 3 %}
{% set naam = pakket.name if pakket.name else 'Onbekende afzender' %}
{% set barcode = pakket.key %}
{% set nette_status = pakket.status_message | default('Status onbekend') %}
{% if 'Pakket is bezorgd' in nette_status %}
{% set nette_status = 'Bezorgd' %}
{% endif %}
{% set postnl_track_url = pakket.url if pakket.url is defined else 'https://www.postnl.nl/tracktrace' %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.postnl_delivery',
'name': '' ~ barcode,
'state': nette_status,
'secondary': 'Bezorgd: ' ~ tijd,
'icon': 'phu:postnl',
'color': '#FB6200',
'tap_action': {
'action': 'url',
'url_path': postnl_track_url
},
'sort_tijd': tijd_ts
} %}
{% set postnl_recent_afgeleverd.list = postnl_recent_afgeleverd.list + [row] %}
{% endif %}
{% endfor %}
{# Eindlijst bouwen #} {% set all_onderweg_items = [] %} {% if
dhl_onderweg.list %}
{% set all_onderweg_items = all_onderweg_items + dhl_onderweg.list %}
{% endif %} {% if postnl_onderweg.list %}
{% set all_onderweg_items = all_onderweg_items + postnl_onderweg.list %}
{% endif %} {% set all_onderweg_items_sorted = all_onderweg_items |
sort(attribute='sort_tijd') %}
{% set all_recent_afgeleverd_items = [] %} {% if
dhl_recent_afgeleverd.list %}
{% set all_recent_afgeleverd_items = all_recent_afgeleverd_items + dhl_recent_afgeleverd.list %}
{% endif %} {% if postnl_recent_afgeleverd.list %}
{% set all_recent_afgeleverd_items = all_recent_afgeleverd_items + postnl_recent_afgeleverd.list %}
{% endif %} {% set all_recent_afgeleverd_items_sorted =
all_recent_afgeleverd_items | sort(attribute='sort_tijd',
reverse=true) %}
[
{% if all_onderweg_items_sorted | count > 0 %}
{
"type": "section",
"label": "Onderweg"
}
{% if all_onderweg_items_sorted | count > 0 %}, {{ all_onderweg_items_sorted | join(',') }}{% endif %}
{% endif %}
{# Add a comma between sections ONLY if both exist #}
{% if (all_onderweg_items_sorted | count > 0) and (all_recent_afgeleverd_items_sorted | count > 0) %},{% endif %}
{% if all_recent_afgeleverd_items_sorted | count > 0 %}
{
"type": "section",
"label": "Recent afgeleverd"
}
{% if all_recent_afgeleverd_items_sorted | count > 0 %}, {{ all_recent_afgeleverd_items_sorted | join(',') }}{% endif %}
{% endif %}
]
show_empty: true
Almost perfect, but I am looking how to get the start-end time for PostNL
And DHL is not showing all the packages, but this is something with DHL, because on ther my.dhl⦠page it doesnāt show either.
(BTW, thanks to the contributors, because only did some copy-paste)
Almost forgot: Another section in my configuration.yaml file:
- platform: template
sensors:
dhl_packages_onderweg:
friendly_name: "DHL pakketjes onderweg"
unit_of_measurement: "pakketjes"
value_template: >
{% set dhl_pakketjes = state_attr('sensor.dhl_packages', 'parcels') or [] %}
{% set undelivered_count = namespace(value=0) %}
{% for pakket in dhl_pakketjes %}
{% set status = pakket.status | lower %}
{% if 'delivered' not in status and 'collected' not in status %}
{% set undelivered_count.value = undelivered_count.value + 1 %}
{% endif %}
{% endfor %}
{{ undelivered_count.value }}
icon_template: "phu:dhl"
attribute_templates:
expect_now: >
{% set pakket = state_attr('sensor.dhl_packages', 'parcels') | rejectattr('category', 'in', ['DELIVERED']) | first or [] %}
{% set tijd_raw = pakket.receivingTimeIndication %}
{% if as_timestamp(now()) > as_timestamp(tijd_raw.start) %}
{% if as_timestamp(now()) < as_timestamp(tijd_raw.end) %}expecting
{% else %}overdue
{% endif %}
{% else %}notexpecting
{% endif %}
deliver_window: >
{% set pakket = state_attr('sensor.dhl_packages', 'parcels') | rejectattr('category', 'in', ['DELIVERED']) | first or [] %}
{% set tijd_raw = pakket.receivingTimeIndication if pakket.receivingTimeIndication is defined else pakket.receivingTimeIndication.moment %}
{% if tijd_raw %}
{{ as_timestamp(tijd_raw.start) | timestamp_custom('%H:%M') }} - {{ as_timestamp(tijd_raw.end) | timestamp_custom('%H:%M') }}
{% else %}Onbekend
{% endif %}
I just enjoyed this great code, and I took the initiative to translate it into English.
type: custom:mod-card
style: |
ha-card {
border-radius: 20px !important;
background-color: rgba(10, 10, 10, 0.4) !important;
}
card:
type: vertical-stack
cards:
- type: heading
heading: Packages
heading_style: title
badges:
- type: entity
show_state: true
show_icon: true
entity: sensor.postnl_delivery
icon: phu:postnl
- type: entity
show_state: true
show_icon: true
entity: sensor.dhl_packages_ontheway
icon: phu:dhl
card_mod:
style: |
ha-card {
margin-top: 10px !important;
margin-right: 10px !important;
margin-left: 20px !important;
}
- type: custom:auto-entities
card:
type: entities
show_header_toggle: false
card_mod:
style: |
ha-card {
background: none;
margin-top: -25px
}
filter:
template: >
{# Fetch DHL data #} {% set dhl_pakketjes =
state_attr('sensor.dhl_packages','parcels') or [] %} {% set
dhl_onderweg = namespace(list=[]) %} {% set dhl_recent_afgeleverd =
namespace(list=[]) %} {# DHL packages #} {% for pakket in
dhl_pakketjes %}
{% set status = pakket.status | lower %}
{% set tijd_raw = pakket.receivingTimeIndication.start if pakket.receivingTimeIndication.start is defined else pakket.receivingTimeIndication.moment %}
{% set tijd_end = pakket.receivingTimeIndication.end if pakket.receivingTimeIndication.end is defined else pakket.receivingTimeIndication.moment %}
{% set tijd = 'Unknown' %}
{% set tijd_ts = 9999999999 %}
{% if tijd_raw %}
{% set tijd_ts = as_timestamp(tijd_raw) %}
{% set tijd = tijd_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% if tijd_raw != tijd_end %}
{% set tijd = tijd_ts | timestamp_custom('%d-%m-%Y %H:%M') ~ ' - ' ~ as_timestamp(tijd_end) | timestamp_custom('%H:%M') %}
{% endif %}
{% endif %}
{% set naam = pakket.sender.name if pakket.sender.name else 'Unknown sender' %}
{% set barcode = pakket.barcode %}
{% set nette_status = pakket.status | replace('_', ' ') | lower() | capitalize() %}
{% if nette_status == 'Prenotification received' %}
{% set nette_status = 'Registered' %}
{% endif %}
{% if nette_status == 'Shipment picked up' %}
{% set nette_status = 'Received by DHL' %}
{% endif %}
{% if nette_status == 'Parcel picked up at parcelshop' %}
{% set nette_status = 'Received by DHL' %}
{% endif %}
{% if nette_status == 'Shipment acceptance parcelshop' %}
{% set nette_status = 'Received by DHL' %}
{% endif %}
{% if nette_status == 'Parcel sorted at hub' %}
{% set nette_status = 'Sorted' %}
{% endif %}
{% if nette_status == 'In transit' %}
{% set nette_status = 'In transit' %}
{% endif %}
{% if nette_status == 'Parcel arrived at local depot' %}
{% set nette_status = 'At local depot' %}
{% endif %}
{% if nette_status == 'Out for delivery' %}
{% set nette_status = 'Out for delivery' %}
{% endif %}
{% if nette_status == 'Not home system intervention delivery at parcelshop' %}
{% set nette_status = 'En route to DHL point' %}
{% endif %}
{% if nette_status == 'Notification for parcelshop collection has been sent' %}
{% set nette_status = 'Ready for pickup at DHL point' %}
{% endif %}
{% if nette_status == 'Delivered' or nette_status == 'Delivered in mailbox' or nette_status == 'Collected at parcelshop' %}
{% set nette_status = 'Delivered' %}
{% set verwacht_of_bezorgd = 'Delivered' %}
{% else %}
{% set verwacht_of_bezorgd = 'Expected' %}
{% endif %}
{% set dhl_track_url = 'https://www.dhl.com/nl-en/home/tracking.html?tracking-id=' ~ barcode ~ '&submit=1' %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.dhl_packages',
'name': '' ~ naam ~ ' - ' ~ barcode,
'state': nette_status,
'secondary': '' ~ verwacht_of_bezorgd ~': ' ~ tijd,
'icon': 'phu:dhl',
'color': '#ffcc00',
'tap_action': {
'action': 'url',
'url_path': dhl_track_url
},
'sort_tijd': tijd_ts
} %}
{% if 'delivered' in status or 'collected' in status %}
{% if pakket.receivedDaysAgo is not none and pakket.receivedDaysAgo <= 3 %}
{% set dhl_recent_afgeleverd.list = dhl_recent_afgeleverd.list + [row] %}
{% endif %}
{% else %}
{% set dhl_onderweg.list = dhl_onderweg.list + [row] %}
{% endif %}
{% endfor %} {# Fetch PostNL data #} {% set postnl_enroute =
state_attr('sensor.postnl_delivery', 'enroute') or [] %} {% set
postnl_delivered = state_attr('sensor.postnl_delivery', 'delivered')
or [] %} {% set postnl_onderweg = namespace(list=[]) %} {% set
postnl_recent_afgeleverd = namespace(list=[]) %}
{# PostNL packages in transit #} {% for pakket in postnl_enroute %}
{% set naam = pakket.name if pakket.name else 'Unknown sender' %}
{% set barcode = pakket.key %}
{% set nette_status = pakket.status_message | default('Status unknown') %}
{% if 'nog niet door PostNL ontvangen of verwerkt' in nette_status %}
{% set nette_status = 'Registered' %}
{% endif %}
{% if 'is ontvangen door PostNL' in nette_status %}
{% set nette_status = 'Shipped' %}
{% endif %}
{% if 'is gesorteerd' in nette_status %}
{% set nette_status = 'Sorted' %}
{% endif %}
{% if 'is onderweg' in nette_status %}
{% set nette_status = 'Out for delivery' %}
{% endif %}
{% if 'Je hebt betaald' in nette_status %}
{% set nette_status = 'Clearance fees paid' %}
{% endif %}
{% set tijd_raw = pakket.planned_from %}
{% set tijd = 'Unknown' %}
{% set tijd_ts = 9999999999 %}
{% if tijd_raw %}
{% set tijd_ts = as_timestamp(tijd_raw) %}
{% set tijd = tijd_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% endif %}
{% set postnl_track_url = pakket.url if pakket.url is defined else 'https://www.postnl.nl/tracktrace' %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.postnl_delivery',
'name': '' ~ naam ~ ' - ' ~ barcode,
'state': nette_status,
'secondary': 'Expected: ' ~ tijd,
'icon': 'phu:postnl',
'color': '#FB6200',
'tap_action': {
'action': 'url',
'url_path': postnl_track_url
},
'sort_tijd': tijd_ts
} %}
{% set postnl_onderweg.list = postnl_onderweg.list + [row] %}
{% endfor %}
{# PostNL recently delivered packages (max 3 days) #} {% for pakket in
postnl_delivered %}
{% set tijd_raw = pakket.delivery_date %}
{% set tijd = 'Unknown' %}
{% set tijd_ts = 0 %}
{% set dagen_verschil = 999 %}
{% if tijd_raw %}
{% set tijd_ts = as_timestamp(tijd_raw) %}
{% set nu = now().timestamp() %}
{% set dagen_verschil = (nu - tijd_ts) / 86400 %}
{% set tijd = tijd_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% endif %}
{% if dagen_verschil <= 3 %}
{% set naam = pakket.name if pakket.name else 'Unknown sender' %}
{% set barcode = pakket.key %}
{% set nette_status = pakket.status_message | default('Status unknown') %}
{% if 'Pakket is bezorgd' in nette_status %}
{% set nette_status = 'Delivered' %}
{% endif %}
{% set postnl_track_url = pakket.url if pakket.url is defined else 'https://www.postnl.nl/tracktrace' %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.postnl_delivery',
'name': '' ~ barcode,
'state': nette_status,
'secondary': 'Delivered: ' ~ tijd,
'icon': 'phu:postnl',
'color': '#FB6200',
'tap_action': {
'action': 'url',
'url_path': postnl_track_url
},
'sort_tijd': tijd_ts
} %}
{% set postnl_recent_afgeleverd.list = postnl_recent_afgeleverd.list + [row] %}
{% endif %}
{% endfor %}
{# Build final list #} {% set all_onderweg_items = [] %} {% if
dhl_onderweg.list %}
{% set all_onderweg_items = all_onderweg_items + dhl_onderweg.list %}
{% endif %} {% if postnl_onderweg.list %}
{% set all_onderweg_items = all_onderweg_items + postnl_onderweg.list %}
{% endif %} {% set all_onderweg_items_sorted = all_onderweg_items |
sort(attribute='sort_tijd') %} {% set all_recent_afgeleverd_items = []
%} {% if dhl_recent_afgeleverd.list %}
{% set all_recent_afgeleverd_items = all_recent_afgeleverd_items + dhl_recent_afgeleverd.list %}
{% endif %} {% if postnl_recent_afgeleverd.list %}
{% set all_recent_afgeleverd_items = all_recent_afgeleverd_items + postnl_recent_afgeleverd.list %}
{% endif %} {% set all_recent_afgeleverd_items_sorted =
all_recent_afgeleverd_items | sort(attribute='sort_tijd',
reverse=true) %} [
{% if all_onderweg_items_sorted | count > 0 %}
{
"type": "section",
"label": "š Enroute"
}
{% if all_onderweg_items_sorted | count > 0 %}, {{ all_onderweg_items_sorted | join(',') }}{% endif %}
{% endif %}
{# Add a comma between sections ONLY if both exist #}
{% if (all_onderweg_items_sorted | count > 0) and (all_recent_afgeleverd_items_sorted | count > 0) %},{% endif %}
{% if all_recent_afgeleverd_items_sorted | count > 0 %}
{
"type": "section",
"label": "š¦ Delivered"
}
{% if all_recent_afgeleverd_items_sorted | count > 0 %}, {{ all_recent_afgeleverd_items_sorted | join(',') }}{% endif %}
{% endif %}
]
show_empty: true
It was fun. If you find anything wrong, please correct me.
thanks guys
This is my PostNL sensor:
sensor:
- platform: template
sensors:
postnl_packages_onderweg:
friendly_name: "PostNL pakketjes onderweg"
unit_of_measurement: "pakketjes"
value_template: >
{% set postnl_pakketjes = state_attr('sensor.postnl_delivery', 'enroute') or [] %}
{% set undelivered_count = namespace(value=0) %}
{% for pakket in postnl_pakketjes %}
{% set undelivered_count.value = undelivered_count.value + 1 %}
{% endfor %}
{{ undelivered_count.value }}
icon_template: "phu:postnl"
attribute_templates:
expect_now: >
{% set pakket = state_attr('sensor.postnl_delivery', 'enroute') | first or [] %}
{% if as_timestamp(now()) > as_timestamp(pakket.planned_from) %}
{% if as_timestamp(now()) < as_timestamp(pakket.planned_to) %}expecting
{% else %}overdue
{% endif %}
{% else %}notexpecting
{% endif %}
deliver_window: >
{% set pakket = state_attr('sensor.postnl_delivery', 'enroute') | first or [] %}
{% set tijd_raw = pakket.planned_from if pakket.planned_from is defined else [] %}
{% if tijd_raw %}
{{ as_timestamp(pakket.planned_from) | timestamp_custom('%H:%M') }} - {{ as_timestamp(pakket.planned_to) | timestamp_custom('%H:%M') }}
{% else %}Onbekend
{% endif %}
tijd_start: >
{% set pakket = state_attr('sensor.postnl_delivery', 'enroute') | first or [] %}
{% set tijd_raw = pakket.planned_from if pakket.planned_from is defined else [] %}
{% if tijd_raw %}
{{ as_timestamp(pakket.planned_from) | timestamp_custom('%d-%m-%Y %H:%M') }}
{% else %}Onbekend
{% endif %}
tijd_verwacht: >
{% set pakket = state_attr('sensor.postnl_delivery', 'enroute') | first or [] %}
{% set tijd_raw = pakket.expected_datetime if pakket.expected_datetime is defined else [] %}
{% if tijd_raw %}
{{ as_timestamp(pakket.expected_datetime) | timestamp_custom('%d-%m-%Y %H:%M') }}
{% else %}Onbekend
{% endif %}
tijd_einde: >
{% set pakket = state_attr('sensor.postnl_delivery', 'enroute') | first or [] %}
{% set tijd_raw = pakket.planned_to if pakket.planned_to is defined else [] %}
{% if tijd_raw %}
{{ as_timestamp(pakket.planned_to) | timestamp_custom('%d-%m-%Y %H:%M') }}
{% else %}Onbekend
{% endif %}
Hi,
I keep getting error 400, does the DHL scrapping work for you ?
Deze fout is ontstaan door een aangepaste integratie.
Logger: custom_components.multiscrape.coordinator
Bron: custom_components/multiscrape/coordinator.py:174
integratie: Multiscrape (documentatie, problemen)
Eerst voorgekomen: 11:37:25 (3 gebeurtenissen)
Laatst gelogd: 13:25:14
DHL packages # Updating failed with exception: Client error '400 Bad Request' for url 'https://my.dhlecommerce.nl/receiver-parcel-api/parcels' For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400
thanks,
Rien
Dit is wat ik in mijn configuration.yaml heb staan (dus geen multiscrape):
command_line:
- sensor:
name: DHL Packages
command: 'curl -v --cookie "X-AUTH-TOKEN=bladiebla" https://my.dhlecommerce.nl/receiver-parcel-api/parcels'
value_template: "{{ value_json.parcels | list | count }}"
json_attributes:
- parcels
En dan uiteraard op bladiebla het token wat ik een keer heb opgehaald (weet uit mijn hoofd niet meer hoe, maar is wel te vinden).
Het enige wat ik vaak moet doen is in de DHL omgeving zelf de trackingcode opvoeren (die ik per mail heb gekregen) om het zichtbaar te krijgen (ook in mijn DHL omgeving).
Hi,
I got warnings regarding this piece of code:
The legacy platform: template syntax for sensor is being removed. Please migrate postnl_packages_onderweg to the modern template syntax.
Step 1 - Remove legacy configuration
Remove the postnl_packages_onderweg template definition from the configuration.yaml sensor: section.
Note: If you are using sensor: !include <filename>.yaml in configuration.yaml, remove the sensor definition from the included <filename>.yaml.
Step 2 - Add the modern configuration
Add new template definition inside configuration.yaml:
template:
- sensor:
- unit_of_measurement: pakketjes
default_entity_id: sensor.postnl_packages_onderweg
icon: phu:postnl
attributes:
expect_now: "{% set pakket = state_attr('sensor.postnl_delivery', 'enroute')
| first or [] %} {% if as_timestamp(now()) > as_timestamp(pakket.planned_from)
%}\n {% if as_timestamp(now()) < as_timestamp(pakket.planned_to) %}expecting\n
\ {% else %}overdue\n {% endif %}\n{% else %}notexpecting {% endif %}"
deliver_window: "{% set pakket = state_attr('sensor.postnl_delivery', 'enroute')
| first or [] %} {% set tijd_raw = pakket.planned_from if pakket.planned_from
is defined else [] %} {% if tijd_raw %}\n {{ as_timestamp(pakket.planned_from)
| timestamp_custom('%H:%M') }} - {{ as_timestamp(pakket.planned_to) | timestamp_custom('%H:%M')
}}\n{% else %}Onbekend {% endif %}"
tijd_start: "{% set pakket = state_attr('sensor.postnl_delivery', 'enroute')
| first or [] %} {% set tijd_raw = pakket.planned_from if pakket.planned_from
is defined else [] %} {% if tijd_raw %}\n {{ as_timestamp(pakket.planned_from)
| timestamp_custom('%d-%m-%Y %H:%M') }}\n{% else %}Onbekend {% endif %}"
tijd_verwacht: "{% set pakket = state_attr('sensor.postnl_delivery', 'enroute')
| first or [] %} {% set tijd_raw = pakket.expected_datetime if pakket.expected_datetime
is defined else [] %} {% if tijd_raw %}\n {{ as_timestamp(pakket.expected_datetime)
| timestamp_custom('%d-%m-%Y %H:%M') }}\n{% else %}Onbekend {% endif %}"
tijd_einde: "{% set pakket = state_attr('sensor.postnl_delivery', 'enroute')
| first or [] %} {% set tijd_raw = pakket.planned_to if pakket.planned_to
is defined else [] %} {% if tijd_raw %}\n {{ as_timestamp(pakket.planned_to)
| timestamp_custom('%d-%m-%Y %H:%M') }}\n{% else %}Onbekend {% endif %}"
name: PostNL pakketjes onderweg
state: "{% set postnl_pakketjes = state_attr('sensor.postnl_delivery', 'enroute')
or [] %} {% set undelivered_count = namespace(value=0) %} {% for pakket in postnl_pakketjes
%}\n {% set undelivered_count.value = undelivered_count.value + 1 %}\n{% endfor
%} {{ undelivered_count.value }}"
Maybe this is of use for someone else.
But Iāv read throughout the tread and trying to reproduce any of the UI results above, but Iām unable to get the last card configurations working. Whilst I do see the results of the sensors and templates.
Cheers,
I got DHL and PostNL working, multiscrape wasnāt working for me, so I just reworked it myself.
I also got DPD, GLS and Dynalogic working, but not sharing it here since it not revelant to this post and takes technical understanding.
Credits to original person who made the frontend, postnl ha, and the dhl auth.
Prerequisites
Make a backup, this should not cause any issues, but just make one.
I am assuming you have tried before so maybe old enties exist, please delete any DHL entities them from Settings > Device & Services > Entities (click show disabled).
Using HACS, Download the following components before starting:
1. Frontend.
- PostNL: Install via HACS (search for āPostNLā by
arjenbos). Configure it via Settings > Devices & Services. - Python Requests: This library is usually included by default in Home Assistant OS.
Created an https://my.dhlecommerce.nl/ account (skip if you already have), then login, afterwards visit this url and if you see json text you can continue.
2. Frontend Resources (Lovelace)
Search for and install these in HACS > Frontend:
card-mod(by thomasloven) ā Required for styling.auto-entities(by thomasloven) ā Required for sorting lists.template-entity-row(by thomasloven) ā Required for the custom list rows.custom-brand-icons(by elax46) ā This setup uses thephu:icon set. If you prefer others, just changephu:tomdi:orsi:in the code.
Step 1: DHL API Calls
This script fetches the raw JSON data from DHL API (free no key needed, just email and password). I advice you to use secrets.yaml but for now you can hardcode them to test.
- Create a folder named
pyscriptinside your/config/folder. - Create a file named
dhl_api.pyinside that folder:/config/pyscript/dhl_api.py. - Paste this code into that file.
# dhl_update.py
import requests
import json
import sys
import os
# --- HARDCODED FOR TEST --- USE SECRETS
DHL_EMAIL = "example2domain.com"
DHL_PASSWORD = "Password"
LOGIN_URL = "https://my.dhlecommerce.nl/api/user/login"
PARCELS_URL = "https://my.dhlecommerce.nl/receiver-parcel-api/parcels"
def fetch_dhl_data():
"""Performs the two-step login and data fetch process."""
session = requests.Session()
login_payload = {"email": DHL_EMAIL, "password": DHL_PASSWORD}
try:
# 1. Login (POST)
login_response = session.post(LOGIN_URL, json=login_payload, timeout=10)
login_response.raise_for_status()
# 2. Fetch Parcels (GET) - Session handles the X-AUTH-TOKEN cookie
headers = {"Content-Type": "application/json"}
parcels_response = session.get(PARCELS_URL, headers=headers, timeout=10)
parcels_response.raise_for_status()
# --- SUCCESS: Print the required JSON to standard output ---
# The command_line sensor will read this output.
return json.dumps(parcels_response.json())
except requests.exceptions.RequestException as e:
# --- FAILURE: Print an empty structure and log the error to sys.stderr ---
sys.stderr.write(f"DHL API Error: {e}\n")
return json.dumps({"parcels": []}) # Return empty list on failure
if __name__ == "__main__":
print(fetch_dhl_data())
Step 2: My DHL Scrapper
I use the Packages method to keep the configuration clean and organized, since i also have DPD, GLS and ParcelApp linked.
1. Enable Packages
Open your configuration.yaml file and add this line under homeassistant:
homeassistant:
packages: !include_dir_named packages
2. Create the DHL Package(as in folder)
Create a new folder called packages (if not already created) in configand create the dhl.yaml file in: /config/packages/dhl.yaml.
Paste this configuration:
Donāt know why VS code gives a warning, but the syntax is correct and works, just ignore.
Change scan_interval: 1800 to what suits you best, dont spam DHL Every second/minute, be reasonable with your requests.
# /config/packages/dhl.yaml
# ----------------------------------------------------
# 1. SCRAPER: Fetches the raw JSON data
# ----------------------------------------------------
command_line:
- sensor:
name: DHL Raw JSON Data
unique_id: dhl_raw_json_data
command: "python3 /config/pyscript/dhl_api.py"
scan_interval: 1800
value_template: "{{ value_json.parcels | count }}"
json_attributes:
- parcels
# ----------------------------------------------------
# 2. LOGIC: Counts packages & calculates attributes safely
# ----------------------------------------------------
template:
- sensor:
- name: "DHL Packages"
unique_id: dhl_packages
unit_of_measurement: "packages"
icon: "phu:dhl"
# --- THE COUNTER ---
# Counts 1 if there is a pre-notification (anything not delivered)
state: >
{% set parcels = state_attr('sensor.dhl_raw_json_data', 'parcels') %}
{% if parcels is none %}
0
{% else %}
{{ parcels
| selectattr('status', 'defined')
| rejectattr('status', 'eq', 'DELIVERED')
| list
| count }}
{% endif %}
attributes:
# --- ATTRIBUTE: EXPECTING NOW? ---
expect_now: >
{% set parcels = state_attr('sensor.dhl_raw_json_data', 'parcels') or [] %}
{% set active_parcels = parcels | rejectattr('category', 'in', ['DELIVERED']) | list %}
{% if active_parcels | count > 0 %}
{% set package = active_parcels[0] %}
{# SAFETY CHECK: Does time indication exist? #}
{% if package.receivingTimeIndication is defined and package.receivingTimeIndication is not none %}
{% set time_raw = package.receivingTimeIndication %}
{# SAFETY CHECK: Do start/end exist inside it? #}
{% if time_raw.start is defined and time_raw.end is defined %}
{% if as_timestamp(now()) > as_timestamp(time_raw.start, 0) %}
{% if as_timestamp(now()) < as_timestamp(time_raw.end, 0) %}expecting
{% else %}overdue
{% endif %}
{% else %}notexpecting
{% endif %}
{% else %}notexpecting
{% endif %}
{% else %}notexpecting
{% endif %}
{% else %}notexpecting
{% endif %}
# --- ATTRIBUTE: DELIVERY WINDOW ---
deliver_window: >
{% set parcels = state_attr('sensor.dhl_raw_json_data', 'parcels') or [] %}
{% set active_parcels = parcels | rejectattr('category', 'in', ['DELIVERED']) | list %}
{% if active_parcels | count > 0 %}
{% set package = active_parcels[0] %}
{# Handle missing time gracefully #}
{% set time_raw = none %}
{% if package.receivingTimeIndication is defined and package.receivingTimeIndication is not none %}
{% set time_raw = package.receivingTimeIndication %}
{% endif %}
{% if time_raw and time_raw.start is defined and time_raw.end is defined %}
{{ as_timestamp(time_raw.start) | timestamp_custom('%H:%M') }} - {{ as_timestamp(time_raw.end) | timestamp_custom('%H:%M') }}
{% else %}Unknown
{% endif %}
{% else %}Unknown
{% endif %}
Step 3: The Dashboard Card
Add a Manual card to your dashboard and paste the code below.
This card handles the logic for:
- Safety Checks: Prevents errors when dates are missing (e.g.,
Prenotification Received, not yet recieved by DHL). - Formatting: detailed date calculations.
- Styling: Custom CSS for a clean look. (Help of Gemini, this CSS was a nightmare)
I know it long, but there is a lot of conditons and logic in it, You can simplify if you donāt want the number of packages to be displayed in the header.
type: custom:mod-card
style: |
ha-card {
border-radius: 20px !important;
background-color: rgba(10, 10, 10, 0.4) !important;
}
card:
type: vertical-stack
cards:
- type: heading
heading: Packages
heading_style: title
badges:
- type: entity
show_state: true
show_icon: true
entity: sensor.postnl_delivery
icon: phu:postnl
- type: entity
show_state: true
show_icon: true
entity: sensor.dhl_packages
icon: phu:dhl
card_mod:
style: |
ha-card {
margin-top: 10px !important;
margin-right: 10px !important;
margin-left: 20px !important;
}
- type: custom:auto-entities
card:
type: entities
show_header_toggle: false
card_mod:
style: |
ha-card {
background: none;
margin-top: -33px;
}
filter:
template: >
{# --- SETUP LISTS --- #}
{% set dhl_packages = state_attr('sensor.dhl_raw_json_data','parcels') or [] %}
{% set postnl_enroute = state_attr('sensor.postnl_delivery', 'enroute') or [] %}
{% set postnl_delivered = state_attr('sensor.postnl_delivery', 'delivered') or [] %}
{% set enroute_list = namespace(items=[]) %}
{% set delivered_list = namespace(items=[]) %}
{# --- PROCESS DHL --- #}
{% for package in dhl_packages %}
{% set status_raw = package.status | lower %}
{% set barcode = package.barcode %}
{% set name = package.sender.name if package.sender.name else 'Unknown sender' %}
{# --- 100% FAIL-SAFE TIME CALCULATION --- #}
{% set time_raw = none %}
{% set time_end = none %}
{# Only proceed if receivingTimeIndication is NOT null #}
{% if package.receivingTimeIndication %}
{% if package.receivingTimeIndication.start is defined %}
{% set time_raw = package.receivingTimeIndication.start %}
{% set time_end = package.receivingTimeIndication.end %}
{% elif package.receivingTimeIndication.moment is defined %}
{% set time_raw = package.receivingTimeIndication.moment %}
{% set time_end = package.receivingTimeIndication.moment %}
{% endif %}
{% endif %}
{% set time_str = 'Unknown' %}
{% set time_ts = 9999999999 %}
{% if time_raw %}
{% set time_ts = as_timestamp(time_raw) %}
{% set time_str = time_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% if time_raw != time_end %}
{% set time_str = time_ts | timestamp_custom('%d-%m-%Y %H:%M') ~ ' - ' ~ as_timestamp(time_end) | timestamp_custom('%H:%M') %}
{% endif %}
{% endif %}
{# --------------------------------------- #}
{# DHL Translation to English #}
{% set clean_status = package.status | replace('_', ' ') | lower() | capitalize() %}
{% if 'prenotification' in clean_status %} {% set clean_status = 'Registered' %} {% endif %}
{% if 'shipment picked up' in clean_status %} {% set clean_status = 'Received by DHL' %} {% endif %}
{% if 'parcel sorted' in clean_status %} {% set clean_status = 'Sorted' %} {% endif %}
{% if 'in transit' in clean_status %} {% set clean_status = 'In transit' %} {% endif %}
{% if 'local depot' in clean_status %} {% set clean_status = 'At local depot' %} {% endif %}
{% if 'out for delivery' in clean_status %} {% set clean_status = 'Out for delivery' %} {% endif %}
{% if 'delivered' in clean_status or 'collected' in clean_status %}
{% set clean_status = 'Delivered' %}
{% set sub_label = 'Delivered' %}
{% else %}
{% set sub_label = 'Expected' %}
{% endif %}
{% set dhl_url = 'https://www.dhl.com/nl-en/home/tracking.html?tracking-id=' ~ barcode ~ '&submit=1' %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.dhl_raw_json_data',
'name': name ~ ' - ' ~ barcode,
'state': clean_status,
'secondary': sub_label ~ ': ' ~ time_str,
'icon': 'phu:dhl',
'color': '#ffcc00',
'tap_action': {'action': 'url', 'url_path': dhl_url},
'sort_time': time_ts
} %}
{% if 'delivered' in status_raw or 'collected' in status_raw %}
{% if package.receivedDaysAgo is not none and package.receivedDaysAgo <= 3 %}
{% set delivered_list.items = delivered_list.items + [row] %}
{% endif %}
{% else %}
{% set enroute_list.items = enroute_list.items + [row] %}
{% endif %}
{% endfor %}
{# --- PROCESS POSTNL (ENROUTE) --- #}
{% for package in postnl_enroute %}
{% set barcode = package.key %}
{% set name = package.name if package.name else 'Unknown sender' %}
{% set status_raw = package.status_message | default('') %}
{# PostNL Translation to English #}
{% set clean_status = status_raw %}
{% if 'nog niet door PostNL' in status_raw %} {% set clean_status = 'Registered' %} {% endif %}
{% if 'is ontvangen' in status_raw %} {% set clean_status = 'Received by PostNL' %} {% endif %}
{% if 'is gesorteerd' in status_raw %} {% set clean_status = 'Sorted' %} {% endif %}
{% if 'is onderweg' in status_raw %} {% set clean_status = 'Out for delivery' %} {% endif %}
{% if 'afgehaald' in status_raw or 'pakketautomaat' in status_raw %} {% set clean_status = 'Ready for pickup' %} {% endif %}
{% if 'betaald' in status_raw %} {% set clean_status = 'Clearance fees paid' %} {% endif %}
{% set time_raw = package.planned_from %}
{% set time_str = 'Unknown' %}
{% set time_ts = 9999999999 %}
{% if time_raw %}
{% set time_ts = as_timestamp(time_raw) %}
{% set time_str = time_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% endif %}
{% set postnl_url = package.url if package.url is defined else 'https://www.postnl.nl/tracktrace' %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.postnl_delivery',
'name': name ~ ' - ' ~ barcode,
'state': clean_status,
'secondary': 'Expected: ' ~ time_str,
'icon': 'phu:postnl',
'color': '#FB6200',
'tap_action': {'action': 'url', 'url_path': postnl_url},
'sort_time': time_ts
} %}
{% set enroute_list.items = enroute_list.items + [row] %}
{% endfor %}
{# --- PROCESS POSTNL (DELIVERED) --- #}
{% for package in postnl_delivered %}
{% set time_raw = package.delivery_date %}
{% set time_ts = 0 %}
{% set days_diff = 999 %}
{% if time_raw %}
{% set time_ts = as_timestamp(time_raw) %}
{% set days_diff = (now().timestamp() - time_ts) / 86400 %}
{% endif %}
{% if days_diff <= 3 %}
{% set barcode = package.key %}
{% set status_raw = package.status_message | default('') %}
{% set clean_status = 'Delivered' %}
{% if 'bezorgd' in status_raw %} {% set clean_status = 'Delivered' %} {% endif %}
{% set postnl_url = package.url if package.url is defined else 'https://www.postnl.nl/tracktrace' %}
{% set time_str = time_ts | timestamp_custom('%d-%m-%Y %H:%M') %}
{% set row = {
'type': 'custom:template-entity-row',
'entity': 'sensor.postnl_delivery',
'name': barcode,
'state': clean_status,
'secondary': 'Delivered: ' ~ time_str,
'icon': 'phu:postnl',
'color': '#FB6200',
'tap_action': {'action': 'url', 'url_path': postnl_url},
'sort_time': time_ts
} %}
{% set delivered_list.items = delivered_list.items + [row] %}
{% endif %}
{% endfor %}
{# --- SORT AND RENDER --- #}
{% set enroute_sorted = enroute_list.items | sort(attribute='sort_time') %}
{% set delivered_sorted = delivered_list.items | sort(attribute='sort_time', reverse=true) %}
[
{% if enroute_sorted | count > 0 %}
{"type": "section", "label": "š Enroute"},
{{ enroute_sorted | join(',') }}
{% endif %}
{% if (enroute_sorted | count > 0) and (delivered_sorted | count > 0) %},{% endif %}
{% if delivered_sorted | count > 0 %}
{"type": "section", "label": "š¦ Delivered"},
{{ delivered_sorted | join(',') }}
{% endif %}
]
show_empty: true
Goes without saying you need to restart HA for the changes to take affect.
Credits to the orginal users, I only reworked it.
Already got postNlL and DHL working.
And linked to my Google calendar.
But I reply because you mentioned DPD and GLS
Can and will you explain how?
Maybe a new tropic?
Let me know
Hey @Jonathanx597 ,
What a great detailled installation procedure. Many thanks.
I do struggle with the fact that Iām not getting any visible icons/list on the UI. In the template I confirmed the āenroute_sortedā| variable has been filled.
but Iām unable to get any UI representation.
Iāve all the Fronted Resourced youāve mentioned installed.
Cheers,



