Displaying Todo List on template to display on Android Widget

My goal was to display a todo list on a widget so that I didn’t have to open the app in order to see my tasks. It uses the new get items service call from the todo list. I had to use a round about approach to do this.

First I created a text helper to store the information in a newline separated list.

Second in automations, service calls can store response data in a local variable. This is my example:

service: todo.get_items
target:
  entity_id:
    - todo.house_list
  device_id: []
  area_id: []
data:
  status: needs_action
response_variable: houselist

I stored the response of the list into the variable “houselist”. You can view the response using the developer tools service calls tester.

Using that variable I was able to forward and store it into the input text helper entity:

alias: input house list to do list
description: ""
trigger:
  - platform: time_pattern
    seconds: "30"
condition: []
action:
  - service: todo.get_items
    target:
      entity_id:
        - todo.house_list
      device_id: []
      area_id: []
    data:
      status: needs_action
    response_variable: houselist
  - service: input_text.set_value
    target:
      entity_id: input_text.houselist
    data:
      value: |
        {% for dict_item in houselist["todo.house_list"]["items"] -%}
          {% for key,value in dict_item.items() -%}
            {% if key == "summary"%}
              {{value}}{{"\n"}}
            {%- endif %}
          {%- endfor %}
        {%- endfor %}
mode: single

This automation updates the text list currently every 30 seconds. Later I will look to update it whenever the count increases or decreases from the todo list. Figuring out how to iterate through the response was difficult and would be easier if you could use variables from the developer tools to the template tester from the service tester.

After loading that into the text helper you can then display using markdown :

type: markdown
content: |-
  {% for item in states('input_text.houselist').split("\n") %}
    <h3>{{item}}</h3>
  {% endfor %}
title: House List

You can create a widget and only include the bracketed portions to display the items in the widget as a list. Currently I was only able to hold around 6 items on the widget with the size 12 but that was good enough for my uses.

1 Like

I’m having the same issue with service response data, and trying to create a dashboard representation. I will also need tap-action support to allow the user to select an item.

Your approach is interesting - have you run into any issues with large response lists? I ask, as I thought there was a 255 character size limit when using the ‘ input_text.set_value’ service.

This was a good post and did help me with moving some strings from the todo shopping list to an ESPhome e-paper display.

I believe this line should look like this:
{% if key == "summary" -%}
Otherwise, it creates 7 extra whitespaces.

But @thlucas is correct, when the string gets more than 255 characters long I noticed that it doesn’t update or changes to unavailable.

My shopping list easily gets over 255 characters, so if you can think of a workaround please post it. My first research found that maybe a MQTT topic could be abused, looks like this one has a 65535 upper limit.

1 Like

@alpharesearch
First, let me apologize for the lengthy post. :smiley:

Most of the following is also documented in my SoundTouchPlus integration wiki, but I copied it here for future reference.

I used a template sensor to get around the size limitation. State values have a limit of 255 characters, but attribute values do not seem to have a limit. What I did was store the epoch date (integer) of when I retrieved the list to the state value of a template sensor, then stored the list itself in the template sensors attribute (see trigger definition below).

I then use the Lovelace HTML Jinja2 Template Card to format simple html to display the list. You can use any other type of card that supports templates though. Please let me know if you run across a card that has tap-action support. That’s the only thing missing from mine that I would like to implement.

How It Works (in my case - modify for yours)

A template trigger is defined (via configuration.yaml) that will fire when the integration updates its soundtouchplus_recents_lastupdated attribute (e.g. an epoch datetime value). The integration updates this attribute whenever the recent_list service is called directly, or when a recentsupdated websocket event is received from the SoundTouch device. The websocket event can occur from a variety of ways: by the SoundTouch App when new content is played; by the press of a preset button on the phsyical device; or by the Python 3 BoseSoundTouchApi play content methods (PlayContentItem, SelectPreset, etc).

The template trigger action calls the integrations’ recent_list service to retrieve the current list of recently played content on the device. The recent list is returned as service response data, which gets stored to a variable named service_response.

The template trigger sensor step updates the sensor.bose_recent_list sensor state to the same value as the soundtouchplus_recents_lastupdated attribute. It also sets the sensor.bose_recent_list sensor serviceResponse attribute with the recent list data.

The template sensor can then be referenced in Lovelace UI cards that support templates.

Limitations

There is currently no tap-action support to select the recent item for play. I am still researching ways to accomplish that - stay tuned.

Requirements

The following are required in order to use this dashboard:

YAML - configuration.yaml

The following must be added to your configuration.yaml (or equivalent template definitions file). It is a trigger that detects when the recent list changes on the device, and updates a template sensor with the updated list. The Lovelace HTML Jinja2 Template Card detects the change, and updates the dashboard.

You will need to change the media_player.your_soundtouch_device_entity_id below to your main SoundTouch device entity_id.

# Bose Recent Station List Update Trigger.
# Detects when the Recent Station List changes, and updates a template sensor with the updated list.
# IMPORTANT - Always set the sensor name and unique_id to match in the sensor step.
  - trigger:
      - platform: state
        entity_id:
          - media_player.your_soundtouch_device_entity_id  # <- change entity_id  
        attribute: soundtouchplus_recents_lastupdated
    action:
      - service: soundtouchplus.recent_list
        data:
          entity_id: "{{ trigger.entity_id }}"
        response_variable: service_response
    sensor:
      - name: bose_recent_list
        unique_id: bose_recent_list
        state: "{{ state_attr(trigger.entity_id, 'soundtouchplus_recents_lastupdated') }}"
        attributes:
          serviceResponse: "{{ service_response }}"

Lovelace UI Card Definition

Add the following YAML (without modifications) to a dashboard of your choice.

type: custom:html-template-card
title: Bose SoundTouch Recent List
ignore_line_breaks: true
always_update: false
content: >
  {% set serviceResponse = None %} 

  {% if has_value('sensor.bose_recent_list'): %}

  {%   set serviceResponse = state_attr('sensor.bose_recent_list','serviceResponse') %}

  {% endif %} 

  {% if serviceResponse == None: %}  
    <div>Recent list is empty</div>
  {% else: %} 

  <style>
    table, td {
        border: none;
        padding: none;
        margin: none;
        text-align: left;
    }  
    .colName {
        display: normal;
        text-align: left;
        vertical-align: middle;
        font-size: larger;
    }
    .colSource {
        display: normal;
        text-align: left;
        vertical-align: middle;
        font-size: xx-small;
    }
    .colHide { 
        display:none;
    }  
    .colWide { 
        padding-left: 6px;
        width: 90%;
    }  
  </style> 

  <table class="colWide">
    <thead class="colHide">
      <tr>
        <th>containerArt</th>
        <th>itemName</th>
        <th>@source</th>
        <th>@type</th>
        <th>@location</th>
        <th>@sourceAccount</th>
      </tr>
    </thead>
    <tbody>{% for recent in serviceResponse['recents']['recent']: %}
      <tr>
       <td><img src="{{ recent['ContentItem']['containerArt'] | default('') }}" width="50" height="50" onerror="this.style.display='none'"></td>
       <td class="colWide">
         <table class="colWide">
           <tr class="colName"><td>{{ recent['ContentItem']['itemName'] | default('') }}</td></tr>
           <tr class="colSource"><td>{{ recent['ContentItem']['@source'] | default('') }} (created on {{ ((recent['@createdOn'] | default(0)) | int) | timestamp_custom('%Y/%m/%d %H:%M:%S') }})</td></tr>
         </table>
       </td>
       <td class="colHide">{{ recent['ContentItem']['itemName'] | default('') }}</td>
       <td class="colHide">{{ recent['ContentItem']['@source'] | default('') }}</td>
       <td class="colHide">{{ recent['ContentItem']['@type'] | default('') }}</td>
       <td class="colHide">{{ recent['ContentItem']['@location'] | default('') }}</td>
       <td class="colHide">{{ recent['ContentItem']['@sourceAccount'] | default('') }}</td>
      </tr>{% endfor %}
    </tbody>
  </table> 

  {% endif %}

Screenshot Example:

Thanks for the information, I did implement it now via the attributes.

For anybody interested here are the changes I made to create the custom attribute in my configuration.yaml, in the homeassistant: customize: section I have !include customize.yaml
In this file, I added:

input_text.shopping_list:
  long_state: ""

Btw, the input_text.shopping_list was created via helpers in the settings.

This creates a new attribute for my input_text.shopping_list entity, the attribute name long_state can be anything.

It looks like there are no native functions to write to an attribute when using an automation, so I had to enable Python scripting first (Python Scripts - Home Assistant) and then I installed the script from this post: How to manually set state/value of sensor? - #7 by jiiins

The script provided me with a new service that I can now use to write to the attribute like this:

service: python_script.set_state
data_template:
  entity_id: input_text.shopping_list
  long_state: |
    {% for dict_item in lshopping_list["todo.shopping_list"]["items"] -%}
      {% for key,value in dict_item.items() -%}
        {% if key == "summary" -%}
          {{value}}{{","}}
        {%- endif %}
      {%- endfor %}
    {%- endfor %}
1 Like