OK, I used the code tag but “three backticks csv” did the trick
Awesome, thank you so much!
I love this and am using it as a base with a few tweaks. The device and playlist backgrounds are hard coded and dont work out of dark mode - wondering if there is a way to detect if dark mode is on or off?
this is my version.
I have removed Black bagrounds from the devices and playlist
Adjusted the track info to be below image like it is in the spotify app
Also made it so on a wider screen the image and info are side by side, and narrower device the image and info are above and below.
See how you guys like this one.
uid: Spotify_widget_0.00
tags: []
props:
parameters:
- description: Title for the widget
label: Static Title
name: title
required: false
type: TEXT
- context: item
label: Select a 'Spotify Player Bridge' (SpotifyPlayerBridge)
name: prefix
required: false
type: TEXT
parameterGroups: []
timestamp: Feb 4, 2021, 7:25:09 PM
component: f7-card
config:
title: =props.title
style:
min-width: 270px
slots:
default:
- component: f7-row
config:
class: margin display-flex align-items-center
slots:
default:
- component: f7-col
config:
width: 100
xsmall: 30
small: 50
medium: 50
large: 50
xlarge: 50
slots:
default:
- component: f7-row
config:
class: '=(items[props.prefix+"_AlbumImage"].state === "NULL")?"display-none" : "- margin-vertical - justify-content-center"'
slots:
default:
- component: oh-image
config:
item: =props.prefix+"_AlbumImage"
style:
width: 70%
- component: f7-col
config:
width: 100
xsmall: 30
small: 50
medium: 50
large: 50
xlarge: 50
slots:
default:
- component: f7-row
config:
class: = (items[props.prefix+"_ActiveDeviceName"].state !== "NULL")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: Label
config:
text: This Spotify Bridge is Unavailable
- component: f7-row
config:
class: = (items[props.prefix+"_ActiveDeviceName"].state === "NULL")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: Label
config:
text: =items[props.prefix+"_MediaArtist"].state || "-"
style:
white-space: nowrap
overflow: hidden
font-size: normal
font-weight: bold
font-style: italic
- component: f7-row
config:
class: = (items[props.prefix+"_ActiveDeviceName"].state === "NULL")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: Label
config:
text: =items[props.prefix+"_MediaTitle"].state || "-"
style:
white-space: nowrap
overflow: hidden
font-size: normal
font-weight: bold
- component: f7-row
config:
class: = (items[props.prefix+"_ActiveDeviceName"].state === "NULL")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: Label
config:
text: =items[props.prefix+"_AlbumName"].state || "-"
style:
white-space: nowrap
overflow: hidden
font-size: normal
font-style: italic
- component: f7-row
config:
style:
position: relative
top: -10px
height: 20px
class:
- justify-content-center
slots:
default:
- component: f7-card
config:
noShadow: true
class: margin display-flex align-items-center
style:
fontSize: 12px
width: calc(100% - 80px)
slots:
default:
- component: Label
config:
text: = items[props.prefix+"_TrackProgressmss"].state
- component: f7-progressbar
config:
color: teal
progress: = 100 * items[props.prefix+"_TrackProgressms"].state / items[props.prefix+"_TrackDurationms"].state
style:
height: 10px
item: props.prefix+"_TrackProgressmss"
- component: Label
config:
text: = items[props.prefix+"_TrackDurationmss"].state
- component: f7-row
config:
class:
- justify-content-space-around
- display-flex
- align-items-center
- align-content-stretch
- margin-top
slots:
default:
- component: f7-icon
config:
f7: '=(props.prefix+"_ActiveDeviceShuffle") ? "shuffle" : ""'
size: 20
color: '=(items[props.prefix+"_ActiveDeviceShuffle"].state === "ON") ? "green" : ""'
style:
position: relative
left: +7%
slots:
default:
- component: oh-button
config:
action: command
actionItem: = props.prefix+"_ActiveDeviceShuffle"
actionCommand: '=(items[props.prefix+"_ActiveDeviceShuffle"].state !== "ON") ? "ON" : "OFF"'
style:
position: absolute
width: 100%
height: 100%
top: 0px
- component: oh-player-item
config:
style:
width: 150px
item: =props.prefix+"_MediaControl"
class:
- display-flex
- margin-
- align-content-stretch
- align-items-center
- justify-content-space-around
- component: f7-icon
config:
f7: '=(props.prefix+"_RepeatMode") ? (items[props.prefix+"_RepeatMode"].state === "context") ? "repeat" : (items[props.prefix+"_RepeatMode"].state === "track") ? "repeat_1" : "repeat" : ""'
size: 20
color: '=(items[props.prefix+"_RepeatMode"].state === "context") ? "green" : (items[props.prefix+"_RepeatMode"].state === "track") ? "green" : ""'
style:
position: relative
left: -8%
slots:
default:
- component: oh-button
config:
action: command
actionItem: = props.prefix+"_RepeatMode"
actionCommand: '=(items[props.prefix+"_RepeatMode"].state === "context") ? "track" : (items[props.prefix+"_RepeatMode"].state === "track") ? "off": "context"'
style:
position: absolute
width: 100%
height: 100%
top: 0px
- component: f7-row
config:
class:
- justify-content-space-around
- display-flex
- align-items-center
- align-content-stretch
- margin-top
slots:
default:
- component: f7-card
config:
noShadow: true
class: margin display-flex align-items-center
style:
height: 20px
fontSize: 20px
width: 100%
slots:
default:
- component: f7-icon
config:
f7: speaker_3
class: margin-horizontal margin
size: 25
- component: oh-slider
config:
label: true
style:
width: 75%
--f7-range-knob-color: rgba(122,122,122,0.8)
--f7-range-bar-size: 12px
--f7-range-bar-border-radius: 6px
--f7-range-knob-size: 16px
--f7-range-bar-bg-color: rgba(122,122,122,0.2)
--f7-range-bar-active-bg-color: linear-gradient(to right, rgba(255,255,255,0), rgba(255,255,255,0.6))
--f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
item: =props.prefix+"_Volume"
- component: f7-row
config:
class: -justify-content-space-around -align-items-center -align-content-stretch
slots:
default:
- component: f7-col
config:
noShadow: true
class: resizable
style:
fontSize: 20px
overflow: hidden
slots:
default:
- component: oh-label-card
config:
icon: f7:hifispeaker
iconSize: 20
action: options
actionItem: =props.prefix+"_ActiveDevices"
item: =props.prefix+"_ActiveDevices"
style:
height: 100%
fontSize: small
- component: f7-col
config:
noShadow: true
class: resizable
style:
fontSize: 20px
overflow: hidden
slots:
default:
- component: oh-label-card
config:
icon: f7:music_note_list
iconSize: 20
action: options
actionItem: =props.prefix+"_Playlists"
item: =props.prefix+"_Playlists"
fontSize: small
Great widget !
I am trying to have it automatically list the “castable” devices when you clikc the “speaker aka output” button, but am not getting consistent results.
If a regular spotify client is casting to a device, the widget sees that and the output shows the cast name.
There are other cast desinations on the network and they do show up with a client, but in the widget, it shows only two - the one playing and a random hardwired one (a mac) that is not in OH3 as anything.
Any ideas on how to get this working would be appreciated.
For me I have a problem with the volume. The slider is not working properly. When I click on “100%” it is only changing to 10 or something like this. I saw, that i can move the slider out of the normal range and then it is working
This might be a general UI issue. I had very similar reports for one of my widgets. See this post here: OH3 Color/White Bulb Widget - #11 by tardismechanic
And I recognized that the devices for spotify are displayed via _ActiveDevices. The problem is that my google home minis seem to standby spotify when not used. So they are in status GONE.
This leads to the situation that I only see the current device and can not switch to another device. Is there any solution to display all spotify devices?
This might have been fixed by Wrong width of oh-slider component in oh-cell · Issue #967 · openhab/openhab-webui · GitHub.
This fix is included in 3.1.M3.
Thanks for the widget, works great!
A possible developement proposal/question: there is a great widget for Habpanel, what do you think about an integration/translation to the OH3 UI? I would love to use/test/tweak it but it is way beyond my knowledge to create it.
I have tried to have a page with open.spotify.com as a web card, but it does not work, I guess Spotify is not allowing embedding the whole player.
Have a great weekend!
@skibro - See this post as a great starting point for an openHAB 3 Main UI Widget; could likely be adapted to do what you want:
Thanks! I have checked that, the multiroom rule + the widget is amazing! I am more looking forward to have basic playlist selection + search functions within the MainUI, but I found a workaround: I have a button which opens the spotify web player in a new tab. Hope someone can found a smoother solution in the future
I have just added a label card with action “external URL”. It opens Spotify Web player in an other chrome tab.
Thanks - after some searching I found the url to be used. Works great!
Thanks for posting your widgets. I mixed the widgets to my needs and want to share it.
spotify.items for convenience
Player Spotify_TrackPlayer "Player" {channel="spotify:player:user1:trackPlayer"}
Dimmer Spotify_Volume "Volume [%s]" {channel="spotify:player:user1:deviceVolume"}
String Spotify_Devices "Active device [%s]" {channel="spotify:player:user1:devices"}
Switch Spotify_DeviceShuffle "Shuffle mode" {channel="spotify:player:user1:deviceShuffle"}
String Spotify_TrackRepeat "Repeat mode: [%s]" {channel="spotify:player:user1:trackRepeat"}
String Spotify_TrackProgress "Track progress: [%s]" {channel="spotify:player:user1:trackProgress"}
String Spotify_TrackDuration "Track duration: [%s]" {channel="spotify:player:user1:trackDuration"}
Number:Time Spotify_TrackProgressms "Track progress: [%s]" {channel="spotify:player:user1:trackProgressMs"}
Number:Time Spotify_TrackDurationms "Track duration: [%s]" {channel="spotify:player:user1:trackDurationMs"}
String Spotify_TrackName "Track Name: [%s]" {channel="spotify:player:user1:trackName"}
String Spotify_AlbumName "Album Name: [%s]" {channel="spotify:player:user1:albumName"}
String Spotify_ArtistName "Artist Name: [%s]" {channel="spotify:player:user1:artistName"}
String Spotify_AlbumImageUrl "Album Art Url" {channel="spotify:player:user1:albumImageUrl"}
String Spotify_Playlists "Playlists [%s]" {channel="spotify:player:user1:playlists"}
String Spotify_PlayName "Playlist [%s]" {channel="spotify:player:user1:playlistName"}
String Spotify_ActiveDeviceName "Active Device Name [%s]" {channel="spotify:player:user1:deviceName"}
String Spotify_ActiveDevices "Active Devices [%s]" {channel="spotify:player:user1:devices"}
Switch Spotify_TrackExplicit "Track explicit [%s]" {channel="spotify:player:user1:trackExplicit"}
String Spotify_TrackUri "Track uri [%s]" {channel="spotify:player:user1:trackUri"}
String Spotify_ArtistUri "Artist uri [%s]" {channel="spotify:player:user1:artistUri"}
String Spotify_AlbumUri "Album uri [%s]" {channel="spotify:player:user1:albumUri"}
uid: Spotify_widget_0.03
tags: []
props:
parameters:
- default: Spotify
description: Title for the widget
label: Static Title
name: title
required: false
type: TEXT
- default: Spotify
description: How all the Items associated with spotify start
label: Item prefix
name: prefix
required: true
type: TEXT
parameterGroups: []
timestamp: Dec 28, 2021, 3:18:59 PM
component: f7-card
config:
title: =props.title
style:
min-width: 270px
slots:
default:
- component: f7-row
config:
class: margin display-flex align-items-center
slots:
default:
- component: f7-col
config:
width: 100
xsmall: 30
small: 50
medium: 50
large: 50
xlarge: 50
slots:
default:
- component: f7-row
config:
class: '=(items[props.prefix+"_AlbumImageUrl"].state === "NULL")?"display-none" : "- margin-vertical - justify-content-center"'
slots:
default:
- component: oh-image
config:
item: =props.prefix+"_AlbumImageUrl"
style:
width: 70%
- component: f7-col
config:
width: 100
xsmall: 30
small: 50
medium: 50
large: 50
xlarge: 50
slots:
default:
- component: f7-row
config:
class: = (items[props.prefix+"_ActiveDeviceName"].state !== "NULL")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: Label
config:
text: This Spotify Bridge is Unavailable
- component: f7-row
config:
class: = (items[props.prefix+"_ActiveDeviceName"].state === "NULL")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: oh-link
config:
text: =items[props.prefix+"_TrackName"].state || "-"
action: url
actionUrl: =items[props.prefix+"_TrackUri"].state
color: teal
style:
white-space: nowrap
overflow: hidden
font-size: normal
font-weight: bold
- component: f7-row
config:
class: = (items[props.prefix+"_ActiveDeviceName"].state === "NULL")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: Label
config:
text: =items[props.prefix+"_ArtistName"].state || "-"
style:
white-space: nowrap
overflow: hidden
font-size: normal
- component: f7-row
config:
class: = (items[props.prefix+"_ActiveDeviceName"].state === "NULL")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: Label
config:
text: =items[props.prefix+"_AlbumName"].state || "-"
style:
white-space: nowrap
overflow: hidden
font-size: normal
font-style: italic
- component: f7-row
config:
class: = (items[props.prefix+"_TrackExplicit"].state != "ON")?"display-none":"display-flex justify-content-center"
slots:
default:
- component: f7-badge
slots:
default:
- component: Label
config:
text: E
- component: f7-row
config:
style:
position: relative
top: -20px
height: 20px
class:
- justify-content-center
slots:
default:
- component: f7-card
config:
noShadow: true
class: margin display-flex align-items-center
style:
fontSize: 12px
width: calc(100% - 80px)
slots:
default:
- component: Label
config:
text: = items[props.prefix+"_TrackProgress"].state
class: margin-right
- component: f7-progressbar
config:
color: teal
progress: = 100 * items[props.prefix+"_TrackProgressms"].state / items[props.prefix+"_TrackDurationms"].state
style:
height: 10px
item: props.prefix+"_TrackProgress"
- component: Label
config:
text: = items[props.prefix+"_TrackDuration"].state
class: margin-left
- component: f7-row
config:
class:
- justify-content-space-around
- display-flex
- align-items-center
- align-content-stretch
- margin-top
slots:
default:
- component: f7-icon
config:
f7: '=(props.prefix+"_DeviceShuffle") ? "shuffle" : ""'
size: 20
color: '=(items[props.prefix+"_DeviceShuffle"].state === "ON") ? "teal" : ""'
style:
position: relative
left: +7%
slots:
default:
- component: oh-button
config:
action: command
actionItem: = props.prefix+"_DeviceShuffle"
actionCommand: '=(items[props.prefix+"_DeviceShuffle"].state !== "ON") ? "ON" : "OFF"'
style:
position: absolute
width: 100%
height: 100%
top: 0px
- component: oh-player-item
config:
style:
width: 150px
item: =props.prefix+"_TrackPlayer"
class:
- display-flex
- margin-
- align-content-stretch
- align-items-center
- justify-content-space-around
- component: f7-icon
config:
f7: '=(props.prefix+"_TrackRepeat") ? (items[props.prefix+"_TrackRepeat"].state === "context") ? "repeat" : (items[props.prefix+"_TrackRepeat"].state === "track") ? "repeat_1" : "repeat" : ""'
size: 20
color: '=(items[props.prefix+"_TrackRepeat"].state === "context") ? "teal" : (items[props.prefix+"_TrackRepeat"].state === "track") ? "teal" : ""'
style:
position: relative
left: -8%
slots:
default:
- component: oh-button
config:
action: command
actionItem: = props.prefix+"_TrackRepeat"
actionCommand: '=(items[props.prefix+"_TrackRepeat"].state === "context") ? "track" : (items[props.prefix+"_TrackRepeat"].state === "track") ? "off": "context"'
style:
position: absolute
width: 100%
height: 100%
top: 0px
- component: f7-row
config:
class:
- justify-content-space-around
- display-flex
- align-items-center
- align-content-stretch
- margin-top
slots:
default:
- component: f7-card
config:
noShadow: true
class: margin display-flex align-items-center
style:
height: 20px
fontSize: 20px
width: 100%
slots:
default:
- component: f7-icon
config:
f7: speaker_3
class: margin-horizontal margin
size: 25
- component: oh-slider
config:
label: true
class: margin
min: 0
max: 100
step: 10
unit: "%"
style:
width: 70%
--f7-range-knob-color: rgba(122,122,122,1)
--f7-range-bar-size: 12px
--f7-range-bar-border-radius: 6px
--f7-range-knob-size: 16px
--f7-range-bar-bg-color: rgba(122,122,122,0.2)
--f7-range-bar-active-bg-color: linear-gradient(to right, rgba(255,255,255,0), rgba(255,255,255,0.6))
--f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
item: =props.prefix+"_Volume"
- component: f7-row
config:
class:
- justify-content-space-around
- align-items-center
- align-content-stretch
- margin
slots:
default:
- component: f7-col
config:
noShadow: true
class: resizable
style:
fontSize: 20px
overflow: hidden
slots:
default:
- component: oh-label-card
config:
icon: f7:hifispeaker
iconSize: 20
action: options
actionItem: =props.prefix+"_ActiveDevices"
item: =props.prefix+"_ActiveDevices"
style:
height: 100%
fontSize: small
- component: f7-col
config:
noShadow: true
class: resizable
style:
fontSize: 20px
overflow: hidden
slots:
default:
- component: oh-label-card
config:
icon: f7:music_note_list
iconSize: 20
action: options
actionItem: =props.prefix+"_Playlists"
item: =props.prefix+"_Playlists"
fontSize: small
I noticed some flaws:
- when playing another playlist than listed a
spotify:playlist:
string is shown in the bottom right select - does not look nice if some props are missing (some
NULL
here and there)
May I ask a stupid question, got the widget working (ok a small problem with the Album Image because I am still running 3.1.0 which has the image where newer versions the reference to the URL, maybe this remark will trigger other people with the same problem) back to the question
The question is: if I look in the HABpanel I do not see this newly made custom widget, what did I not do yet?
Hello,
thank you all for that nice widget!
I have one issue:
When no spotify device is connected to and there is no music track played OR while playing a podcast track, the picture of the item _AlbumImageUrl shows a “broken picture” icon, because there is nothing found what could be shown or as default. Is there a possibility to show a default or alternative picture via url path? e.g. a local saved picture?
slots:
default:
- component: oh-image
config:
item: =props.prefix+"_AlbumImageUrl"
Regards.
Newer openhab cores are now auto renaming the items to have under scores so the above would be…
=props.prefix+“_Album_Image_Url”
The first post seems to have a newer widget that means you need to specify each item. Just a heads up to anyone using old widget code and then re-creates the items with the newer naming.