Edit: Added oh-repeater function to replace current speaker placeholder and reduce code by 50%. Also this allows to use unlimited zone players with the new version.
In this topic i want to share my Sonos player with you. It is my replacement of the sonos controller app. In order to use the full features of this widget you need to use my multiroom rule. Without this the grouping and ungrouping of players will not work.
How to use the widget:
In order to use all features of this widget you need to use my multiroom rule that is described here.
https://community.openhab.org/t/sonos-multiroom-rule-for-openhab-3/108305
You can just copy my code below in a new widget. When you run the widget you can provide all items to it. as speaker array you need to provide your details in the following format. The speakers must be provided in the following format. Please ensure that the " around the item is still there. (The zoneName and all items inside the zoneArray must have the same name as provided in the linked multiroom rule.)
- ["<zoneName>", “<…_player>”, “<…_volume>”, “<…_coordinator>”]
- [“living room”; “Sonos_livingroom_player”; “Sonos_livingroom_volume”; “Sonos_livingroom_coordinator”][“bed room”, “Sonos_bedroom_player”; “Sonos_bedroom_volume”; “Sonos_bedroom_coordinator”]
If you are using my sonos multiroom volume rule you should provide as volume item the zoneVolume item that was created for the rule. (Only on the item selector if the widget. Inside the item array keep the original volume item so that the widget can control both.)
The widget code
uid: widget_Sonos_Player
tags: []
props:
parameters:
- description: (optional) widget title
label: (optional) widget title
name: propWidgetTitle
required: false
type: TEXT
- context: item
description: item to display the title
label: item title
name: itemTitle
required: true
type: TEXT
- context: item
description: (optional) item to display the album
label: (optional) item album
name: itemAlbum
required: false
type: TEXT
- context: item
description: (optional) item to display the artist
label: (optional) item artist
name: itemArtist
required: false
type: TEXT
- context: item
description: (optional) item to display a cover image
label: (optional) item cover image
name: itemCover
required: false
type: TEXT
- context: item
description: (optional) item to control the player
label: (optional) item player
name: itemPlayer
required: false
type: TEXT
- context: item
description: (optional) item to control the volume
label: (optional) item volume
name: itemVolume
required: false
type: TEXT
- context: item
description: (optional) item for shuffle option
label: (optional) item shuffle
name: itemShuffle
required: false
type: TEXT
- context: item
description: (optional) item for repeat option
label: (optional) item repeat
name: itemRepeat
required: false
type: TEXT
- description: (optional) Name of the zone player
label: (optional) Zone name
name: propZoneName
required: false
type: TEXT
advanced: true
- context: item
description: (optional) item of the zone coordinator channel
label: (optional) item coordinator
name: itemCoordinator
required: false
type: TEXT
advanced: true
- description: (optional) speaker array like this -> ["<zoneName>", "<player>", "<volume>", "<coordinator>"]["<zoneName2>", "<player2>", "<volume2>", "<coordinator2>"]
label: (optional) speaker array
name: propZoneArray
required: false
type: TEXT
advanced: true
- context: item
description: (optional) item for the sonos mulltiroom rule. e.g. Sonos_Multiroom_Control
label: (optional) item to control the multiroom control rule
name: itemSonosRule
required: false
type: TEXT
advanced: true
parameterGroups: []
timestamp: Dec 22, 2020, 11:23:18 AM
component: f7-card
config:
title: =props.propWidgetTitle
slots:
default:
- component: f7-card-content
slots:
default:
- component: f7-row
slots:
default:
- component: Label
config:
text: "Title: "
class:
- display-flex
- component: f7-row
config:
style:
position: relative
top: -10px
height: 30px
class:
- justify-content-center
slots:
default:
- component: Label
config:
text: =items[props.itemTitle].displayState || items[props.itemTitle].state
style:
fontSize: 28px
white-space: nowrap
overflow: hidden
- component: f7-row
config:
visible: "=(props.itemAlbum) ? true : false"
slots:
default:
- component: Label
config:
text: "Album: "
class:
- display-flex
- component: f7-row
config:
visible: "=(props.itemAlbum) ? true : false"
style:
position: relative
top: -10px
height: 30px
class:
- justify-content-center
slots:
default:
- component: Label
config:
text: =items[props.itemAlbum].displayState || items[props.itemAlbum].state
style:
fontSize: 28px
white-space: nowrap
overflow: hidden
- component: f7-row
config:
visible: "=(props.itemArtist) ? true : false"
slots:
default:
- component: Label
config:
text: "Artist: "
class:
- display-flex
- component: f7-row
config:
visible: "=(props.itemArtist) ? true : false"
style:
position: relative
top: -10px
height: 30px
class:
- justify-content-center
slots:
default:
- component: Label
config:
text: =items[props.itemArtist].displayState || items[props.itemArtist].state
style:
fontSize: 28px
white-space: nowrap
overflow: hidden
- component: f7-row
config:
visible: "=(props.itemCover) ? true : false"
class:
- margin-vertical
- justify-content-center
slots:
default:
- component: oh-image
config:
item: =props.itemCover
style:
width: 70%
- component: f7-row
config:
visible: "=(props.itemPlayer) ? true : false"
class:
- justify-content-space-around
- display-flex
- align-items-center
- align-content-stretch
- margin-top
style:
position: relative
top: +5px
slots:
default:
- component: f7-icon
config:
f7: '=(props.itemShuffle) ? "shuffle" : ""'
size: 20
color: '=(items[props.itemShuffle].state === "ON") ? "green" : ""'
style:
position: relative
left: +7%
slots:
default:
- component: oh-button
config:
action: command
actionItem: =props.itemShuffle
actionCommand: '=(items[props.itemShuffle].state !== "ON") ? "ON" : "OFF"'
style:
position: absolute
width: 100%
height: 100%
top: 0px
- component: oh-player-item
config:
style:
width: 150px
item: =props.itemPlayer
class:
- display-flex
- margin-
- align-content-stretch
- align-items-center
- justify-content-space-around
- component: f7-icon
config:
f7: '=(props.itemRepeat) ? (items[props.itemRepeat].state === "ALL") ? "repeat" : (items[props.itemRepeat].state === "ONE") ? "repeat_1" : "repeat" : ""'
size: 20
color: '=(items[props.itemRepeat].state === "ALL") ? "green" : (items[props.itemRepeat].state === "ONE") ? "green" : ""'
style:
position: relative
left: -8%
slots:
default:
- component: oh-button
config:
action: command
actionItem: =props.itemRepeat
actionCommand: '=(items[props.itemRepeat].state === "ALL") ? "ONE" : (items[props.itemRepeat].state === "ONE") ? "OFF": "ALL"'
style:
position: absolute
width: 100%
height: 100%
top: 0px
- component: f7-row
config:
visible: "=(props.itemVolume) ? true : false"
class:
- justify-content-space-around
- display-flex
- align-items-center
- align-content-stretch
slots:
default:
- component: f7-card
config:
noShadow: true
class: margin display-flex align-items-center
style:
fontSize: 20px
min-width: calc(100% - 20px)
slots:
default:
- component: f7-icon
config:
f7: speaker_3
class: margin-horizontal margin
size: 30
- component: oh-slider
config:
label: true
style:
height: +40px
width: calc(100% - 35px)
min: 0
item: =props.itemVolume
class:
- display-flex
- margin-horizontal
- align-content-stretch
- align-items-center
- justify-content-space-around
- component: f7-button
config:
class: '=(props.propZoneArray) ? "" : "display-none"'
style:
position: absolute
height: 100%
width: 100%
top: 0px
popoverOpen: .popoverVolume
slots:
default:
- component: f7-popover
config:
class: popoverVolume
style:
min-width: 350px
slots:
default:
- component: oh-repeater
config:
for: zoneVolume
in: =props.propZoneArray.split("]")
containerClasses:
- display-flex
- flex-direction-column
slots:
default:
- component: f7-card
config:
class: '=(loop.zoneVolume.split("\"")[1] && items[loop.zoneVolume.split("\"")[7]].state === items[props.itemCoordinator].state) ? "display-flex flex-direction-row justify-content-flex-start align-items-center" : "display-none"'
style:
height: 40px
slots:
default:
- component: Label
config:
style:
fontSize: 20px
class:
- margin-left
text: =(loop.zoneVolume.split("\"")[1])
- component: oh-slider
config:
label: true
style:
height: +40px
width: calc(100% - 40%)
min: 0
item: =(loop.zoneVolume.split("\"")[5])
class:
- display-flex
- margin
- align-content-stretch
- align-items-center
- component: f7-row
config:
class: '=(props.propZoneName && props.itemPlayer) ? "justify-content-space-around align-items-center align-content-stretch" : "display-none"'
style:
position: relative
top: -26px
height: 50px
slots:
default:
- component: f7-card
config:
noShadow: true
class: display-flex align-items-center
style:
fontSize: 20px
min-width: 130px
slots:
default:
- component: f7-icon
config:
f7: hifispeaker
size: 30
class: margin
- component: Label
config:
class: margin-right
text: =props.propZoneName
style:
fontSize: 20px
- component: f7-icon
config:
f7: '=(items[props.itemPlayer].state === "PLAY") ? "chart_bar_alt_fill" : ""'
class: margin-right
size: 20
- component: f7-button
config:
class: '=(props.propZoneArray) ? "" : "display-none"'
style:
position: absolute
height: 100%
width: 100%
top: 0px
popoverOpen: .popoverPlayer
slots:
default:
- component: f7-popover
config:
class: popoverPlayer
style:
min-width: 280px
slots:
default:
- component: oh-repeater
config:
for: zoneSpeaker
in: =props.propZoneArray.split("]")
containerClasses:
- display-flex
- flex-direction-column
slots:
default:
- component: f7-card
config:
class: '=(loop.zoneSpeaker.split("\"")[1]) ? "display-flex flex-direction-row align-items-center" : "display-none"'
style:
height: 40px
slots:
default:
- component: oh-button
config:
style:
position: absolute
top: 0px
width: 100%
height: 100%
action: command
actionItem: =props.itemSonosRule
actionCommand: '=(items[loop.zoneSpeaker.split("\"")[7]].state === items[props.itemCoordinator].state) ? "Remove:" + loop.zoneSpeaker.split("\"")[1] + "" : "Add:" + loop.zoneSpeaker.split("\"")[1] + "@" + props.propZoneName'
- component: f7-col
config:
class: margin display-flex flex-direction-row align-items-center
slots:
default:
- component: f7-icon
config:
f7: '=(items[loop.zoneSpeaker.split("\"")[7]].state === items[props.itemCoordinator].state) ? "checkmark_alt_circle_fill" : "circle"'
- component: Label
config:
class: margin-left
text: =loop.zoneSpeaker.split("\"")[1]
style:
fontSize: 20px
- component: f7-col
config:
class: margin align-items-center
slots:
default:
- component: f7-icon
config:
f7: '=(items[loop.zoneSpeaker.split("\"")[3]].state === "PLAY") ? "chart_bar_alt_fill" : ""'