PostNL Integration

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
Schermafbeelding 2025-09-15 220116

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.
Schermafbeelding 2025-09-16 012013

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.
Schermafbeelding 2025-09-16 194825

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 %}
1 Like

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 %}

1 Like

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).