@alpharesearch
First, let me apologize for the lengthy post.
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: