[How-To] Fetch and display Garbage Collection (Sophämtning) for Stockholm, Sweden (SVOA API)

Hi everyone,

I noticed there is a lack of clear documentation on how to reliably fetch and display garbage collection data specifically for Stockholm, Sweden (Stockholm Vatten och Avfall / SVOA). Instead of relying on third-party custom components that might break, I found a way to query their API directly using Home Assistant’s native REST sensor.

I wanted to share a complete guide on how to get this data, along with a bonus template for a very compact and dynamic dashboard card.

Step 1: Finding your API URL

SVOA uses a simple open API endpoint for searches, but your address needs to be URL-encoded. The base URL is: https://www.stockholmvattenochavfall.se/villa-och-radhus/avfallstjanster/nar-kommer-sopbilen/Search?address=

Add your address, area, and zip code to the end, replacing spaces with %20 and Swedish letters with their URL-encoded counterparts (å = %C3%A5, ä = %C3%A4, ö = %C3%B6). Example for “Exempelgatan 1, Stockholm, 12345”: .../Search?address=Exempelgatan%201,%20Stockholm,%2012345

Step 2: The REST Configuration

You can add this directly under the rest: domain in your configuration.yaml, or in a separate rest.yaml file (if you use rest: !include rest.yaml).

This code fetches the JSON data every 6 hours and uses Jinja loops to dynamically find the correct waste types (General, Food, and Garden waste) and extract the Date, Frequency, and Weekday.

- resource: "https://www.stockholmvattenochavfall.se/villa-och-radhus/avfallstjanster/nar-kommer-sopbilen/Search?address=Miklag%C3%A5rdsv%C3%A4gen%2012,%20Bromma,%2016855"
  scan_interval: 21600
  headers:
    User-Agent: "Mozilla/5.0"
    Accept: "application/json"
  sensor:
    # ==========================================
    # 1. RESTAVFAfellowLL
    # ==========================================
    - name: "Soptömning Restavfall Datum"
      unique_id: soptomning_restavfall_datum
      icon: mdi:trash-can
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Restavfall Frekvens"
      unique_id: soptomning_restavfall_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Restavfall Veckodag"
      unique_id: soptomning_restavfall_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Restavfall' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 2. MATAVFALL
    # ==========================================
    - name: "Soptömning Matavfall Datum"
      unique_id: soptomning_matavfall_datum
      icon: mdi:food-apple
      value_template: >
        {% set ns = namespace(val='Ej aktiv') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Matavfall Frekvens"
      unique_id: soptomning_matavfall_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Matavfall Veckodag"
      unique_id: soptomning_matavfall_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Okänd') %}
        {% for key, value in value_json.items() %}
          {% if 'Matavfall' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    # ==========================================
    # 3. TRÄDGÅRDSAVFALL
    # ==========================================
    - name: "Soptömning Trädgård Datum"
      unique_id: soptomning_tradgard_datum
      icon: mdi:leaf
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].ExecutionDate %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Trädgård Frekvens"
      unique_id: soptomning_tradgard_frekvens
      icon: mdi:update
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].FetchFrequency %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

    - name: "Soptömning Trädgård Veckodag"
      unique_id: soptomning_tradgard_veckodag
      icon: mdi:calendar-today
      value_template: >
        {% set ns = namespace(val='Ej säsong') %}
        {% for key, value in value_json.items() %}
          {% if 'Trädgård' in key %}
            {% set ns.val = value[0].Weekday %}
          {% endif %}
        {% endfor %}
        {{ ns.val }}

Restart Home Assistant after adding this to generate the sensors.

Step 3: The Dashboard Card (Bonus)

If you have a wall-mounted tablet (Kiosk mode), standard cards often take up too much space. Here is a highly compressed, dynamic card to display the data we just fetched.

Features of this card:

  • Icon colors change automatically (Grey = Winter break/inactive, Blue/Brown/Green = Normal, Orange = 3 days left, Red = 1 day left or Today).
  • Injects a dynamic countdown text right next to the name (Idag), (Imorgon), (Om X dgr), or (Vinteruppehåll).

Requirements for the card: Install template-entity-row and card-mod via HACS Frontend.

The Code:

type: entities
title: Avfallshämtning
card_mod:
  style: |
    #states > * {
      margin-top: -10px !important;
      margin-bottom: -10px !important;
    }
entities:
  - type: custom:template-entity-row
    entity: sensor.soptomning_restavfall_datum
    icon: mdi:trash-can
    name: >
      {% set datum = states('sensor.soptomning_restavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} Restavfall (Ej aktiv) {% elif
      dagar <= 0 %} Restavfall (Idag) {% elif dagar == 1 %} Restavfall (Imorgon)
      {% else %} Restavfall (Om {{ dagar }} dgr) {% endif %}
    state: >
      {{ states('sensor.soptomning_restavfall_veckodag') }} {{
      states('sensor.soptomning_restavfall_datum') }}
    color: >
      {% set datum = states('sensor.soptomning_restavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} grey {% elif dagar <= 0 %} red
      {% elif dagar <= 3 %} orange {% else %} dodgerblue {% endif %}
  - type: custom:template-entity-row
    entity: sensor.soptomning_matavfall_datum
    icon: mdi:food-apple
    name: >
      {% set datum = states('sensor.soptomning_matavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} Matavfall (Ej aktiv) {% elif
      dagar <= 0 %} Matavfall (Idag) {% elif dagar == 1 %} Matavfall (Imorgon)
      {% else %} Matavfall (Om {{ dagar }} dgr) {% endif %}
    state: >
      {{ states('sensor.soptomning_matavfall_veckodag') }} {{
      states('sensor.soptomning_matavfall_datum') }}
    color: >
      {% set datum = states('sensor.soptomning_matavfall_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej aktiv'] %} grey {% elif dagar <= 0 %} red
      {% elif dagar <= 3 %} orange {% else %} brown {% endif %}
  - type: custom:template-entity-row
    entity: sensor.soptomning_tradgard_datum
    icon: mdi:leaf
    name: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej säsong'] %} Trädgård (Ej säsong) {% elif
      dagar > 30 %} Trädgård (Vinteruppehåll) {% elif dagar <= 0 %} Trädgård
      (Idag) {% elif dagar == 1 %} Trädgård (Imorgon) {% else %} Trädgård (Om {{
      dagar }} dgr) {% endif %}
    state: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% if datum
      in ['unavailable', 'unknown', 'Ej säsong'] %} 
        -
      {% else %}
        {{ states('sensor.soptomning_tradgard_veckodag') }} {{ datum }}
      {% endif %}
    color: >
      {% set datum = states('sensor.soptomning_tradgard_datum') %} {% set ts =
      as_timestamp(datum ~ ' 00:00:00', 0) %} {% set now_ts =
      as_timestamp(now().strftime('%Y-%m-%d 00:00:00'), 0) %} {% set dagar =
      ((ts - now_ts) / 86400) | round(0) | int %} {% if ts == 0 or datum in
      ['unavailable', 'unknown', 'Ej säsong'] or dagar > 30 %} grey {% elif
      dagar <= 0 %} red {% elif dagar <= 3 %} orange {% else %} #4CAF50 {% endif
      %}
view_layout:
  position: sidebar

Hope this helps anyone who need a garbage collect card in Stockholm out! Let me know if you have any questions.