DLNA / UPNP binding

Yes… forgot to mention that. IDK if the issue is in the binding, or the UIs and StateOptions. I’ll check to see if the Kodi binding has the same issue.

The issue is in the UI’s. I remember a PR where the state options were added to Basic UI and Classic UI in selections, but the PR did not add dynamic updating of the state options. I believe the SSE events don’t include them, so the issue was in the core. PaperUI has the same issue.

1 Like

@5iver does the UPNP binding give you the ability to find renderers on the network and effecively use these devices as AudioSinks/players for Audio on the network?

Yes… it’s great not having to use scripts for this anymore.

Fantastic, do the Sinks show as switches that can be turned on/off? Id love to see how youve used it fo this purpose if you use them as sinks for audio playback

No. Audiosinks are more at the system level, that can be used in audio Actions. I have Items that hold the UUIDs of all the sinks, an Item for each speaker representing the PlayURI, and play to the speakers using this. The important part is right above the ‘except’ in the third rule.

@rule("Alert: Advance House Shuffle")
@when("Item HouseShuffle_Advance received command")
def advanceHouseShuffle(event):
    advanceHouseShuffle.log.debug("Advancing House Shuffle")
    workingStreamList = str(items["Working_Stream_List"]).split(",")
    if len(workingStreamList) == 0:
        streamList = str(items["Stream_List"]).split(",")
        workingStreamList = refillList(None, streamList, "HA_Streams_{}".format(items["HouseShuffle_Genre"]))
    if len(workingStreamList) > 0:
        events.sendCommand("HouseShuffle_PlayURI", workingStreamList.pop())
        events.postUpdate("Working_Stream_List", ",".join(workingStreamList))
    else:
        advanceHouseShuffle.log.error("Advance House Shuffle: Reset workingStreamList failed")  

@rule("Alert: House Shuffle received command")
@when("Item HouseShuffle_PlayURI received command")
def houseShuffleChange(event):
    previousPlayURI = items["HouseShuffle_PlayURI"]
    houseShuffleChange.log.debug("House Shuffle changed: [{}]".format(event.itemCommand))
    for speaker in filter(lambda speaker: speaker.state == previousPlayURI and ir.getItem(speaker.name.replace("_PlayURI", "_Player")).state == PlayPauseType.PLAY, ir.getItem("gSpeakerPlayURI").getMembers()):
        speaker.sendCommand(str(event.itemCommand))

@rule("Alert: Speaker playURI received command")
@when("Member of gSpeakerPlayURI received command")
def speakerPlayURIReceivedCommand(event):
    speakerPlayURIReceivedCommand.log.debug("Speaker playURI received command [{}]: Starting rule: current state=[{}], command=[{}]".format(event.itemName,ir.getItem(event.itemName).state,event.itemCommand))
    try:
        if event.itemCommand == StringType("HouseShuffle"):
            newURI = str(items["HouseShuffle_PlayURI"])
            if not any(filter(lambda speaker: speaker.state == PLAY and items[speaker.name.replace("_Player","_PlayURI")] == StringType(newURI), ir.getItem("gSpeakerPlayer").getMembers())):
                speakerPlayURIReceivedCommand.log.debug("Speaker playURI received command [{}]: No other speaker is playing the house shuffle, so picking a new random one".format(event.itemName))
                workingStreamList = str(items["Working_Stream_List"]).split(",")
                if len(workingStreamList) > 0:
                    newURI = workingStreamList.pop()
                    if len(workingStreamList) > 0:
                        events.sendCommand("Working_Stream_List", ",".join(workingStreamList))
                    else:
                        events.sendCommand("HouseShuffle_Genre", str(items["HouseShuffle_Genre"]))
                    events.postUpdate("HouseShuffle_PlayURI", newURI)
            events.sendCommand(event.itemName, newURI)
        else:
            if items[event.itemName.replace("_PlayURI","_System")] == ON:
                audioSink = "upnpcontrol:upnprenderer:{}".format(items[event.itemName.replace("_PlayURI","_UUID")])
                playStream(audioSink, str(event.itemCommand))
            else:
                speakerPlayURIReceivedCommand.log.debug("Speaker playURI received command [{}]: canceling request since speaker is offline: [{}]".format(event.itemName, event.itemCommand))
    except Exception as e:
        import traceback
        message = "Alert: Speaker playURI received command [{}]: Exception: [{}]: [{}]".format(event.itemName, e, traceback.format_exc())
        speakerPlayURIReceivedCommand.log.error(message)
        NotificationAction.sendNotification(adminEmail,"{}".format(message))

While I’m at it, here is a rule to provide a online/offline state for the speakers. Every now and then mine will fall off the network.

@rule("Alert: Speaker system status update")
@when("Thing upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f521099e changed")
@when("Thing upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f521366d changed")
@when("Thing upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f522dcaf changed")
@when("Thing upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f5207868 changed")
@when("Thing upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f520f682 changed")
@when("Thing upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f5210909 changed")
@when("Thing upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52220b3 changed")
@when("Thing upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52109de changed")
def speakerSystemStatusUpdate(event):
    speakerName = filter(lambda item: str(item.state).replace("uuid:", "") == str(event.thingUID).split(":")[2], ir.getItem("gSpeakerUUID").members)[0].name.replace("UUID", "System")
    speakerSystemStatusUpdate.log.debug("Speaker system update [{}]: [{}]".format(speakerName, event.statusInfo))
    if items[speakerName] == OnOffType.OFF and str(event.statusInfo) == "ONLINE":
        events.sendCommand(speakerName, "ON")
        if items[speakerName.replace("System", "Player")] == PlayPauseType.PLAY:
            events.sendCommand(speakerName.replace("System", "Stop"), "OFF")
        else:
            events.sendCommand(speakerName.replace("System", "Stop"), "ON")
        speakerSystemStatusUpdate.log.info("Speaker system update [{}]: ON".format(speakerName))
    elif items[speakerName] == OnOffType.ON:
        events.sendCommand(speakerName, "OFF")
        speakerSystemStatusUpdate.log.info("Speaker system update [{}]: OFF".format(speakerName))
2 Likes

Woah! Thanks!!! May you also show us your items?

They are not your usual DSL rules are they??

I will have a crack at doing this with my UPNP renderer

No, Jython. I’ve liberated my home of the old rule engine! :wink:

Here are some examples of the Items I use…

Group	                                    gSpeaker	                                "Speakers"	                                            <soundvolume>	    (gEntertainment,gDevice)
    Group                                       gAutoPlay_Mode                              "Auto Play: Mode [%s]"                                  <none>              (gSpeaker)
    Group                                       gSpeakerSystem                              "Renderer: System [%s]"                                 <none>              (gSpeaker)
    Group	                                    gSpeakerIP	                                "Renderer: IPs"	                                        <none>	            (gSpeaker)
    Group	                                    gSpeakerPlayURI	                            "Renderer: PlayURIs"	                                <none>	            (gSpeaker)
    Group	                                    gSpeakerPlayer	                            "Renderer: Players"	                                    <none>	            (gSpeaker)
    Group	                                    gSpeakerStop	                            "Renderer: Stops"	                                    <none>	            (gSpeaker)
    Group	                                    gSpeakerTitle	                            "Renderer: Titles"	                                    <none>	            (gSpeaker)
    Group	                                    gSpeakerUUID	                            "Renderer: UUIDs"	                                    <none>	            (gSpeaker)
    Group	                                    gSpeakerVolume	                            "Renderer: Volumes"	                                    <none>	            (gSpeaker)

String    Alert_Prefix                     "Alert prefix [%s]"                                 <none>          (gSpeaker)
String    Working_Stream_List              "Working stream list [%s]"                          <none>          (gSpeaker)
String    Stream_List                      "Stream list [%s]"                                  <none>          (gSpeaker)
String    Working_Prefix_List              "Working prefix list [%s]"                          <none>          (gSpeaker)
String    Prefix_List                      "Prefix list [%s]"                                  <none>          (gSpeaker)
String    HouseShuffle_PlayURI             "House Shuffle station [%s]"                        <none>          (gSpeaker)
String    HouseShuffle_Genre               "House Shuffle station genre [%s]"                  <none>          (gSpeaker)
String    HouseShuffle_Playlist            "House Shuffle playlist [%s]"                       <none>          (gSpeaker)
Switch    HouseShuffle_Advance             "Advance House Shuffle"                             <switch>        (gSpeaker)         ["Switchable"]      {autoupdate="false"}
Switch    Wakeup_Alarm                     "Wakeup Alarm [%s]"                                 <switch>        (gSpeaker)         ["Switchable"]
Switch    Wakeup_Alarm_Snooze              "Snooze"                                            <switch>        (gSpeaker)         ["Switchable"]      {autoupdate="false"}
Switch    Stop_All_Speakers                "Stop all speakers"                                 <switch>        (gSpeaker)         ["Switchable"]      {autoupdate="false"}

String    US_MasterBathroom_Speaker_AutoPlay_Mode   "Master Bathroom Upstairs (Auto Play: Mode) [%s]"       <switch>                (gUS_MasterBathroom,gSpeaker,gAutoPlay_Mode)
String    US_MasterBathroom_Speaker_PlayURI         "Master Bathroom Upstairs (PlayURI) [%s]"               <none>                  (gUS_MasterBathroom,gSpeaker,gSpeakerPlayURI)
String    US_MasterBathroom_Speaker_UUID            "Master Bathroom Upstairs (UUID) [%s]"                  <none>                  (gUS_MasterBathroom,gSpeaker,gSpeakerUUID)
Switch    US_MasterBathroom_Speaker_System          "Master Bathroom Upstairs (System) [%s]"                <switch>                (gUS_MasterBathroom,gSpeaker,gSpeakerSystem)
Dimmer    US_MasterBathroom_Speaker_Volume          "Master Bathroom Upstairs (Volume) [%s]"                <soundvolume>           (gUS_MasterBathroom,gSpeaker,gSpeakerVolume)     ["Lighting"]    {channel="upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52109de:volume"}
Player    US_MasterBathroom_Speaker_Player          "Master Bathroom Upstairs (Player) [%s]"                <none>                  (gUS_MasterBathroom,gSpeaker,gSpeakerPlayer)                     {channel="upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52109de:control"}
Switch    US_MasterBathroom_Speaker_Stop            "Master Bathroom Upstairs (Stop) [%s]"                  <switch>                (gUS_MasterBathroom,gSpeaker,gSpeakerStop)                       {channel="upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52109de:stop"}
Switch    US_MasterBathroom_Speaker_Mute            "Master Bathroom Upstairs(Mute) [%s]"                   <soundvolume_mute>      (gUS_MasterBathroom,gSpeaker)                                    {channel="upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52109de:mute"}
String    US_MasterBathroom_Speaker_Title           "Master Bathroom Upstairs (Title) [%s]"                 <none>                  (gUS_MasterBathroom,gSpeaker,gSpeakerTitle)                      {channel="upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52109de:title"}
String    US_MasterBathroom_Speaker_Artist          "Master Bathroom Upstairs (Artist) [%s]"                <none>                  (gUS_MasterBathroom,gSpeaker)                                    {channel="upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52109de:artist"}
String    US_MasterBathroom_Speaker_Album           "Master Bathroom Upstairs (Album) [%s]"                 <none>                  (gUS_MasterBathroom,gSpeaker)                                    {channel="upnpcontrol:upnprenderer:5f9ec1b3-ed59-1900-4530-0007f52109de:album"}

String  Twonky_Renderer                 "Twonky Renderer [%s]"          <none>   (gDS_Office,gSpeaker)   {channel="upnpcontrol:upnpserver:55076f6e-6b79-1d65-a4eb-00089bd0e8f2:upnprenderer"}
String  Twonky_Title                    "Twonky Title [%s]"             <none>   (gDS_Office,gSpeaker)   {channel="upnpcontrol:upnpserver:55076f6e-6b79-1d65-a4eb-00089bd0e8f2:currenttitle"}
String  Twonky_Search_Criteria          "Twonky Search Criteria [%s]"   <none>   (gDS_Office,gSpeaker)   {channel="upnpcontrol:upnpserver:55076f6e-6b79-1d65-a4eb-00089bd0e8f2:searchcriteria"}
Switch  Twonky_Search                   "Twonky Search [%s]"            <none>   (gDS_Office,gSpeaker)   {channel="upnpcontrol:upnpserver:55076f6e-6b79-1d65-a4eb-00089bd0e8f2:search"}
Switch  Twonky_Select                   "Twonky Select [%s]"            <none>   (gDS_Office,gSpeaker)   {channel="upnpcontrol:upnpserver:55076f6e-6b79-1d65-a4eb-00089bd0e8f2:select"}
Switch  Twonky_Serve                    "Twonky Serve [%s]"             <none>   (gDS_Office,gSpeaker)   {channel="upnpcontrol:upnpserver:55076f6e-6b79-1d65-a4eb-00089bd0e8f2:serve"}
2 Likes

doh! That leaves me out :frowning:
Ive got enough to learn on the old engine to start that hahah!

That’s unfortunate. You can use both at the same time too.

The important bit for you, using the DSL, would be something like this…

import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "Speaker playURI received command"
when
    Member of gSpeakerPlayURI received command
then
    audioSink = "upnpcontrol:upnprenderer:" + ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name.replace("_PlayURI","_UUID")).state.toString
    playStream(audioSink, receivedCommand)
end
1 Like

Only new to this stuff, so just baby steps. If only I was a developer/programmer it would be much easier but one language is enough for now!

Thanks Scott! Ill give it a go

Hi Mark,

Sorry to bother you but just to know if there is any chance that you could check this issue ?

Best regards,

Mac_Fly.

1 Like

HI 5iver, had a go at this but I could only get MUTE to work :slight_smile:


20:01:55.429 [INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'KDS_Stop' received command ON
20:01:55.430 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Handle command ON for channel upnpcontrol:upnprenderer:4c494e4e-0026-0f22-3898-012366860171:stop on renderer Linn Klimax DS
20:01:55.430 [INFO ] [arthome.event.ItemStatePredictedEvent] - KDS_Stop predicted to become ON
20:01:55.431 [DEBUG] [nding.upnpcontrol.handler.UpnpHandler] - Upnp device Linn Klimax DS invoke upnp action Stop on service AVTransport with inputs {InstanceID=0}

It seems maybe this feature isnt available to me?

0:02:33.266 [DEBUG] [nding.upnpcontrol.handler.UpnpHandler] - Upnp device Linn Klimax DS add upnp subscription on AVTransport
20:02:33.278 [DEBUG] [nding.upnpcontrol.handler.UpnpHandler] - Upnp device Linn Klimax DS received subscription reply true from service AVTransport
20:02:33.286 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable LastChange with value <Event xmlns="urn:schemas-upnp-org:metadata-1-0/AVT/"><InstanceID val="0"><TransportState val="NO_MEDIA_PRESENT"/><TransportStatus val="OK"/><CurrentMediaCategory val="NO_MEDIA"/><PlaybackStorageMedium val="NONE"/><NumberOfTracks val="0"/><CurrentTrack val="0"/><CurrentTrackDuration val="0:00:00"/><CurrentMediaDuration val="0:00:00"/><CurrentTrackURI val=""/><AVTransportURI val=""/><CurrentTrackMetaData val=""/><AVTransportURIMetaData val=""/><PossiblePlaybackStorageMedia val="NETWORK"/><CurrentPlayMode val="NORMAL"/><TransportPlaySpeed val="1"/><NextAVTransportURI val="NOT_IMPLEMENTED"/><NextAVTransportURIMetaData val="NOT_IMPLEMENTED"/><RecordStorageMedium val="NOT_IMPLEMENTED"/><PossibleRecordStorageMedia val="NOT_IMPLEMENTED"/><RecordMediumWriteStatus val="NOT_IMPLEMENTED"/><CurrentRecordQualityMode val="NOT_IMPLEMENTED"/><PossibleRecordQualityModes val="NOT_IMPLEMENTED"/></InstanceID></Event> from service AVTransport
20:02:33.287 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentMediaCategory with value NO_MEDIA from service AVTransport
20:02:33.288 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable AVTransportURI with value  from service AVTransport
20:02:33.288 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentTrackURI with value  from service AVTransport
20:02:33.288 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable TransportPlaySpeed with value 1 from service AVTransport
20:02:33.289 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentTrackURI with value  from service AVTransport
20:02:33.289 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable InstanceID with value 0 from service AVTransport
20:02:33.289 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentTrackMetaData with value  from service AVTransport
20:02:33.290 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable TransportStatus with value OK from service AVTransport
20:02:33.290 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable AVTransportURIMetaData with value  from service AVTransport
20:02:33.290 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentTrackMetaData with value  from service AVTransport
20:02:33.291 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentTrackDuration with value 0:00:00 from service AVTransport
20:02:33.291 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentPlayMode with value NORMAL from service AVTransport
20:02:33.291 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable PossiblePlaybackStorageMedia with value NETWORK from service AVTransport
20:02:33.292 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentTrack with value 0 from service AVTransport
20:02:33.292 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentRecordQualityMode with value NOT_IMPLEMENTED from service AVTransport
20:02:33.292 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable RecordMediumWriteStatus with value NOT_IMPLEMENTED from service AVTransport
20:02:33.293 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable PlaybackStorageMedium with value NONE from service AVTransport
20:02:33.293 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable NextAVTransportURIMetaData with value NOT_IMPLEMENTED from service AVTransport
20:02:33.293 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable PossibleRecordQualityModes with value NOT_IMPLEMENTED from service AVTransport
20:02:33.294 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable NumberOfTracks with value 0 from service AVTransport
20:02:33.294 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable PossibleRecordStorageMedia with value NOT_IMPLEMENTED from service AVTransport
20:02:33.294 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable CurrentMediaDuration with value 0:00:00 from service AVTransport
20:02:33.295 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable NextAVTransportURI with value NOT_IMPLEMENTED from service AVTransport
20:02:33.295 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable RecordStorageMedium with value NOT_IMPLEMENTED from service AVTransport
20:02:33.295 [DEBUG] [npcontrol.handler.UpnpRendererHandler] - Upnp device Linn Klimax DS received variable TransportState with value NO_MEDIA_PRESENT from service AVTransport

Please post your Item definition and describe what you are trying to do.

Hi Scott

Adjust volume, skip track or stop

Switch    KDS_Stop   "KDS Stop [%s]"                       {channel="upnpcontrol:upnprenderer:4c494e4e-0026-0f22-3898-012366860171:stop"}
Switch    KDS_Mute   "KDS Mute [%s]"                       {channel="upnpcontrol:upnprenderer:4c494e4e-0026-0f22-3898-012366860171:mute"}
String    KDS_Title  "KDS Track Title [%s]"                {channel="upnpcontrol:upnprenderer:4c494e4e-0026-0f22-3898-012366860171:title"}
Player    KDS_Player "KDS Player [%s]"                     {channel="upnpcontrol:upnprenderer:4c494e4e-0026-0f22-3898-012366860171:control"}
Dimmer    KDS_Volume "KDS Volume [%s]"                     {channel="upnpcontrol:upnprenderer:4c494e4e-0026-0f22-3898-012366860171:volume"}

1 Like

Those look OK. It’s possible that your device and the binding are not compatible. What speaker are you trying to use?

Hi Scott

Its one of these:

These are made by Linn, so I would have thought they’d work. Can you use them with any other UPnP control software? I’ve tried a LOT of them and this is my favorite.

I use Kazoo to control the unit. They do work with Bubble UPNP too.

How can I determine which features it would support? Not sure where to go from here…

Since this is a very experimental binding, so there’s not much more you can do without digging into the logs and code.