DLNA / UPNP binding

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.

No worries! thanks Scott :slight_smile: Ill leave it be until a proper binding is developed. I dont have such skills im afraid

@Mherwege, do you think you will be doing anything more with your wonderful binding? I really don’t see why this it shouldn’t be included in the distro. No matter how rough on the edges it is, I’ve been very happily using it for quite a while without issues. If you are not interested in developing it, I’d be happy to do some minor cleanup, including migrating to bnd, and get it submitted. Or if you are planning to move it forward, I could just submit some PRs into your repo. Moving automaton forward has been a very slow and steady stream of roadblocks, so I’m looking for something else to work on and this binding definitely deserves some attention!

1 Like

@5iver I would be very happy for you to take it over. I believe I already have it migrated to bnd in my online repo, but not sure it is complete or entirely working. I have a upnp branch in my repo that contains it. It is old, but you can try starting from that one. I may have done some later work on it and not pushed it. I lost the setup on my laptop since and the backup does not have the full git history. I will try to compare if I got any further and let you know.
I think the main architectural gripe I have with it is that I don’t do anything with the acknowledgements and error responses on the messages sent. But that would mean wrapping all messages sent in completable futures, so major rearchitecting.
Feel free to pick up from here, and happy to assist where I can. But I don’t have the bandwidth to own it.

1 Like

@5iver I spend a little bit of time on it. The status on my github repo (upnp branch) is now the most recent development work I have. It compiles into the 2.5.5 snapshot. I did not correct all (non-blocking) code issues reported. I also did not do any test if the functionality is still working. Some of my last functional developments on this (about a year ago) may have left some of it in a non-working state. I don’t remember the exact status anymore. I suggest you try if it still works and take it from there.

1 Like