Changes in / [20:30]


Ignore:
Location:
/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • /trunk/L_Sonos1.lua

    r20 r30  
    22 
    33local http = require("socket.http") 
     4 
    45-- 5 Second timeout 
    56http.TIMEOUT = 5 
     
    1213</s:Envelope>]] 
    1314 
    14 UPNP_AVTRANSPORT_SID = 'urn:schemas-upnp-org:service:AVTransport:1' 
    15 UPNP_RENDERING_CONTROL_SID = 'urn:schemas-upnp-org:service:RenderingControl:1' 
    16 UPNP_DEVICE_PROPERTIES_SID = 'urn:schemas-upnp-org:service:DeviceProperties:1' 
     15UPNP_AVTRANSPORT_SERVICE = 'urn:schemas-upnp-org:service:AVTransport:1' 
     16UPNP_RENDERING_CONTROL_SERVICE = 'urn:schemas-upnp-org:service:RenderingControl:1' 
     17UPNP_DEVICE_PROPERTIES_SERVICE = 'urn:schemas-upnp-org:service:DeviceProperties:1' 
     18 
     19UPNP_AVTRANSPORT_SID = 'urn:upnp-org:serviceId:AVTransport' 
     20UPNP_RENDERING_CONTROL_SID = 'urn:upnp-org:serviceId:RenderingControl' 
     21UPNP_DEVICE_PROPERTIES_SID = 'urn:upnp-org:serviceId:DeviceProperties' 
     22 
     23local DIDL_FORMAT=[[<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"> 
     24<item id="%s" parentID="%s" restricted="true"> 
     25<res protocolInfo="x-file-cifs:*:audio/mpeg:*">%s</res> 
     26<r:streamContent>%s</r:streamContent> 
     27<upnp:albumArtURI>%s</upnp:albumArtURI> 
     28<dc:title>%s</dc:title> 
     29<upnp:class>%s</upnp:class> 
     30<dc:creator>%s</dc:creator> 
     31<upnp:album>%s</upnp:album> 
     32<upnp:originalTrackNumber>%s</upnp:originalTrackNumber> 
     33<r:albumArtist>%s</r:albumArtist> 
     34</item> 
     35</DIDL-Lite> 
     36]] 
    1737 
    1838local log = print 
     
    2444end 
    2545 
    26 function decode(val) 
     46local function decode(val) 
    2747      return val:gsub("&amp;", "&") 
    2848                :gsub("&lt;", "<") 
    2949                :gsub("&gt;", ">") 
    3050                :gsub("&quot;", '"') 
    31     end 
     51end 
     52 
     53local function encode(val) 
     54      return val:gsub("&", "&amp;") 
     55                :gsub("<", "&lt;") 
     56                :gsub(">", "&gt;") 
     57                :gsub('"', "&quot;") 
     58end 
     59 
     60function createDIDL(resURI, creator, album, title, artist, streamContent, albumArtURI, trackNumber, class, id, parentID) 
     61  return DIDL_FORMAT:format(encode(id or 1), 
     62                            encode(parentID or -1), 
     63                            encode(resURI or ""), 
     64                            encode(streamContent or ""), 
     65                            encode(albumArtURI or ""), 
     66                            encode(title or "No title"), 
     67                            encode(class or "object.item.audioItem.musicTrack"), 
     68                            encode(creator or ""), 
     69                            encode(album or ""), 
     70                            encode(trackNumber or 1), 
     71                            encode(artist or "No artist")) 
     72end 
    3273 
    3374-- event url, user-agent, callback, nt, TimeOut 
    34 function UPnP_subscribe(ipAddress, ipPort) 
    35  
     75function UPnP_subscribe(ipAddress, ipPort, serverURL, callbackURL) 
    3676  -- 
    3777  -- Execute the subscription 
    3878  -- 
    3979  local status, statusMsg = http.request{ 
    40     method = "SUBSCRIBE /MediaRenderer/RenderingControl/Event HTTP/1.1", 
    41     headers = {["HOST"] = ipAddress .. ":" .. ipPort, 
    42                ["CALLBACK"] = "<http://192.168.1.15/cmh/event>", 
    43                ["NT"] = "upnp:event",}, 
    44     } 
     80    url = serverURL:format(ipAddress, ipPort), 
     81    method = "SUBSCRIBE", 
     82    headers = {["HOST"] = string.format("%s:%s", ipAddress, ipPort), 
     83               ["CALLBACK"] = string.format("<%s>", callbackURL), 
     84               ["NT"] = "upnp:event"} 
     85  } 
     86 
    4587  return status 
    4688end   
     
    58100                :gsub('"', "&quot;") 
    59101    end 
    60      
     102 
    61103    local result = "" 
    62104 
     
    118160    -- Handle TIMEOUT 
    119161    -- 
    120     return {} 
     162    return false, statusMsg or "An error occurred during the UPnP call" 
    121163  end 
    122164   
     
    125167    -- Handle SUCCESS 
    126168    -- 
    127      
    128     if (data == nil or data == "") then 
    129       return "" 
     169    if (data == nil or data == "") then 
     170      return true, "" 
    130171    else 
    131172      local pattern = string.format('.*<s:Body><u:%sResponse xmlns:u="urn:(.*)">(.*)</u:%sResponse></s:Body></s:Envelope>.*', action, action) 
     
    134175      -- TODO: Handle Non-Scalar results 
    135176      -- luup.log("Before gmatch: " .. data) 
    136       local sid, value 
     177      local sid, value 
    137178      for serviceId, result in data:gmatch(pattern) do 
    138179        -- luup.log(serviceId .. " --> " .. result) 
     
    141182 
    142183      -- commented out and returns a simple string instead. Due to multiple tags (not nested though) 
    143 --      [[local dataTable = {} 
    144 --      for tagBegin, value, tagEnd in value:gmatch("<(.*)>(.*)</(.*)>") do 
    145  --       print(tagBegin .. " -> " .. value .. " <- " .. tagEnd) 
    146  --       dataTable[tagBegin] = value 
    147  --     end 
    148  
    149   --     return dataTable]] 
    150        return decode(value) 
    151        
     184      --      [[local dataTable = {} 
     185      --      for tagBegin, value, tagEnd in value:gmatch("<(.*)>(.*)</(.*)>") do 
     186      --        print(tagBegin .. " -> " .. value .. " <- " .. tagEnd) 
     187      --        dataTable[tagBegin] = value 
     188      --      end 
     189 
     190      --     return dataTable]] 
     191      return true, decode(value) 
    152192    end 
    153193  else 
  • /trunk/S_Sonos1.xml

    r20 r30  
    1414    <action> 
    1515      <name>SetVolume</name> 
    16         <argumentList> 
    17           <argument> 
    18             <name>NewVolume</name> 
    19             <direction>in</direction> 
    20             <relatedStateVariable>ui4</relatedStateVariable> 
    21           </argument> 
    22         </argumentList> 
    23     </action> 
    24     <action> 
     16      <argumentList> 
     17        <argument> 
     18          <name>NewVolume</name> 
     19          <direction>in</direction> 
     20          <relatedStateVariable>ui4</relatedStateVariable> 
     21          </argument> 
     22      </argumentList> 
     23    </action> 
     24    <action> 
    2525      <name>SetFileToPlay</name> 
    26         <argumentList> 
    27           <argument> 
    28             <name>FileToPlay</name> 
    29             <direction>in</direction> 
    30             <relatedStateVariable>string</relatedStateVariable> 
    31           </argument> 
    32         </argumentList> 
    33     </action> 
    34     <action> 
     26      <argumentList> 
     27        <argument> 
     28          <name>FileToPlay</name> 
     29          <direction>in</direction> 
     30          <relatedStateVariable>string</relatedStateVariable> 
     31        </argument> 
     32      </argumentList> 
     33    </action> 
     34    <action> 
    3535      <name>SetURIToPlay</name> 
    36         <argumentList> 
    37           <argument> 
    38             <name>URIToPlay</name> 
    39             <direction>in</direction> 
    40             <relatedStateVariable>string</relatedStateVariable> 
    41           </argument> 
    42         </argumentList> 
    43     </action> 
     36      <argumentList> 
     37        <argument> 
     38          <name>URIToPlay</name> 
     39          <direction>in</direction> 
     40          <relatedStateVariable>string</relatedStateVariable> 
     41        </argument> 
     42      </argumentList> 
     43    </action> 
     44    <action> 
     45      <name>Say</name> 
     46      <argumentList> 
     47        <argument> 
     48          <name>Text</name> 
     49          <direction>in</direction> 
     50          <relatedStateVariable>string</relatedStateVariable> 
     51        </argument> 
     52        <argument> 
     53          <name>Language</name> 
     54          <direction>in</direction> 
     55          <relatedStateVariable>string</relatedStateVariable> 
     56        </argument> 
     57      </argumentList> 
     58    </action> 
    4459  </actionList> 
    4560</scpd> 
  • /trunk/D_Sonos1.xml

    r20 r30  
    2424 
    2525      <service> 
     26        <serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType> 
     27        <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     28      </service> 
     29 
     30      <service> 
    2631        <serviceType>urn:micasaverde-com:service:Volume:1</serviceType> 
    2732        <serviceId>urn:micasaverde-com:serviceId:Volume1</serviceId> 
  • /trunk/I_Sonos1.xml

    r20 r30  
    33 
    44<functions> 
    5   local isMuted = true 
     5  local url = require("socket.url") 
     6 
     7  local MSG_CLASS = "Sonos" 
     8 
    69  local DEBUG_MODE = true 
     10  local taskHandle = -1 
     11 
     12  local TASK_ERROR = 2 
     13  local TASK_ERROR_PERM = -2 
     14  local TASK_SUCCESS = 4 
     15  local TASK_BUSY = 1 
     16 
     17  local PARENT_DEVICE 
    718 
    819  local upnp 
    9    
     20 
     21  local HADEVICE_SID = "urn:micasaverde-com:serviceId:HaDevice1" 
     22 
    1023  local ipPort = 1400 -- Sonos 
    1124  local UPNP_AVTRANSPORT_URL = 'http://%s:%s/MediaRenderer/AVTransport/Control' 
    1225  local UPNP_RENDERING_CONTROL_URL = 'http://%s:%s/MediaRenderer/RenderingControl/Control' 
    1326  local UPNP_DEVICE_PROPERTIES_URL = 'http://%s:%s/DeviceProperties/Control' 
    14    
     27  local UPNP_RENDERING_EVENT_URL = 'http://%s:%s/MediaRenderer/RenderingControl/Event' 
     28 
     29  -- Derived from: 
     30  --    http://sonosIP:1400/xml/device_description.xml 
     31  -- 
     32  local SONOS_AUDIOINPUT_URL = 'http://%s:%s/AudioIn/Control' 
     33  local SONOS_AUDIOINPUT_SERVICE = 'urn:schemas-upnp-org:service:AudioIn:1' 
     34  local SONOS_AUDIOINPUT_SID = 'urn:upnp-org:serviceId:AudioIn' 
     35  
    1536  -- local ipPort = 8888 -- Onkyo Receiver 
    1637  -- local UPNP_AVTRANSPORT_URL = 'http://%s:%s/upnp_control_2' 
     
    1839 
    1940  local function log(stuff, level) 
    20     luup.log("Sonos: " .. stuff, (level or 50)) 
     41    luup.log(string.format("%s: %s", MSG_CLASS, stuff), (level or 50)) 
    2142  end 
    2243 
    2344  local function debug(stuff) 
    2445    if (DEBUG_MODE) then 
    25       log("debug " .. stuff, 1) 
     46      log("debug " .. stuff, 35) 
    2647    end 
     48  end 
     49 
     50  local function task(text, mode) 
     51    luup.log("task " .. text) 
     52    if (mode == TASK_ERROR_PERM) then 
     53      taskHandle = luup.task(text, TASK_ERROR, MSG_CLASS, taskHandle) 
     54    else 
     55      taskHandle = luup.task(text, mode, MSG_CLASS, taskHandle) 
     56 
     57      -- Clear the previous error, since they're all transient 
     58      if (mode ~= TASK_SUCCESS) then 
     59        luup.call_delay("clearTask", 30, "", false) 
     60      end 
     61    end 
     62  end 
     63 
     64  -- 
     65  -- Has to be "non-local" in order for MiOS to call it :( 
     66  -- 
     67  function clearTask() 
     68    task("Clearing...", TASK_SUCCESS) 
    2769  end 
    2870 
    2971  -- Low level xml element extracter. Borrowed from @cedriclocqueneux example.  Enhanced to handle xml == nil 
    3072  local function extractElement(tag, xml, default) 
    31     if xml == nil then  
    32       return(default)  
    33     else 
    34       local pattern = "&lt;"..tag..">(.*)&lt;/"..tag..">" 
     73    if (xml == nil) then 
     74      return(default) 
     75    else 
     76      local pattern = "&lt;"..tag..">(.*)&lt;/"..tag..">" 
    3577      local result = (string.match(xml, pattern) or default) 
    3678      return result 
    37     end 
    38   end 
    39    
     79    end 
     80  end 
     81 
     82  local function setIfChanged(serviceId, name, value, deviceId) 
     83      local curValue = luup.variable_get(serviceId, name, deviceId) 
     84 
     85      if ((value ~= curValue) or (curValue == nil)) then 
     86          luup.variable_set(serviceId, name, value, deviceId) 
     87          return true 
     88      else 
     89          return false 
     90      end 
     91  end 
     92 
     93  function refreshCache() 
     94    debug("refreshCache: start") 
     95    local status, tmp 
     96    local changed = false 
     97    local ip = luup.devices[PARENT_DEVICE].ip 
     98 
     99    -- 
     100    -- Loop every 15 seconds to refresh the cache 
     101    -- 
     102    luup.call_delay("refreshCache", 15) 
     103 
     104    -- 
     105    -- The next section reads various status on the Sonos Zone and populates variables on the device. For use by UI or Mute function, etc. 
     106    -- Is only executed upon initial load of module for now though 
     107    -- Get ZoneName 
     108    status, tmp = upnp.UPnP_request(ip, ipPort, UPNP_DEVICE_PROPERTIES_URL, 
     109      "GetZoneAttributes", upnp.UPNP_DEVICE_PROPERTIES_SERVICE, {CurrentZoneName="", CurrentIcon=""}) 
     110 
     111    if (status ~= true) then 
     112        debug("refreshCache: Device offline? status=" .. tmp) 
     113        return 
     114    end 
     115 
     116    changed = changed or setIfChanged("urn:schemas-micasaverde-com:device:avmisc:1", "ZoneName", extractElement("CurrentZoneName", tmp, ""), PARENT_DEVICE) 
     117                      or setIfChanged(upnp.UPNP_DEVICE_PROPERTIES_SID, "ZoneName", extractElement("CurrentZoneName", tmp, ""), PARENT_DEVICE) 
     118                      or setIfChanged(upnp.UPNP_DEVICE_PROPERTIES_SID, "Icon", extractElement("CurrentIcon", tmp, ""), PARENT_DEVICE) 
     119 
     120    -- GetCurrentTransportState  (PLAYING, STOPPED, etc) 
     121    status, tmp = upnp.UPnP_request(ip, ipPort, UPNP_AVTRANSPORT_URL, 
     122      "GetTransportInfo", upnp.UPNP_AVTRANSPORT_SERVICE, {InstanceID="0", CurrentTransportState="", CurrentTransportStatus="", CurrentSpeed="1"}) 
     123    changed = changed or setIfChanged("urn:schemas-micasaverde-com:device:avmisc:1", "CurrentTransportState", extractElement("CurrentTransportState", tmp, ""),  PARENT_DEVICE) 
     124                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "TransportState", extractElement("CurrentTransportState", tmp, ""),  PARENT_DEVICE) 
     125                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "TransportStatus", extractElement("CurrentTransportStatus", tmp, ""),  PARENT_DEVICE) 
     126                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "TransportPlaySpeed", extractElement("CurrentSpeed", tmp, ""),  PARENT_DEVICE) 
     127 
     128    -- Get Playmode (NORMAL, REPEAT_ALL, SHUFFLE_NOREPEAT, SHUFFLE) 
     129    status, tmp = upnp.UPnP_request(ip, ipPort, UPNP_AVTRANSPORT_URL, 
     130      "GetTransportSettings", upnp.UPNP_AVTRANSPORT_SERVICE, {InstanceID="0", PlayMode="", ReqQualityMode=""}) 
     131    changed = changed or setIfChanged("urn:schemas-micasaverde-com:device:avmisc:1", "PlayMode", extractElement("PlayMode", tmp, ""), PARENT_DEVICE) 
     132                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "CurrentPlayMode", extractElement("PlayMode", tmp, ""), PARENT_DEVICE) 
     133                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "CurrentRecordQualityMode", extractElement("RecQualityMode", tmp, ""), PARENT_DEVICE) 
     134 
     135    -- Get Current Transport Actions (a CSV of valid Transport Action/Transitions) 
     136    status, tmp = upnp.UPnP_request(ip, ipPort, UPNP_AVTRANSPORT_URL, 
     137      "GetCurrentTransportActions", upnp.UPNP_AVTRANSPORT_SERVICE, {InstanceID="0"}) 
     138    changed = changed or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "CurrentTransportActions", extractElement("Actions", tmp, ""), PARENT_DEVICE) 
     139 
     140    -- Get Audio Line in settings 
     141    status, tmp = upnp.UPnP_request(ip, ipPort, SONOS_AUDIOINPUT_URL, 
     142      "GetAudioInputAttributes", SONOS_AUDIOINPUT_SERVICE, {InstanceID="0"}) 
     143    changed = changed or setIfChanged(SONOS_AUDIOINPUT_SID, "AudioInputName", extractElement("CurrentName", tmp, ""), PARENT_DEVICE) 
     144                      or setIfChanged(SONOS_AUDIOINPUT_SID, "Icon", extractElement("CurrentIcon", tmp, ""), PARENT_DEVICE) 
     145 
     146    -- Get Media Information 
     147    status, tmp = upnp.UPnP_request(ip, ipPort, UPNP_AVTRANSPORT_URL, 
     148      "GetMediaInfo", upnp.UPNP_AVTRANSPORT_SERVICE, {InstanceID="0"}) 
     149    changed = changed or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "NumberOfTracks", extractElement("NrTracks", tmp, ""), PARENT_DEVICE) 
     150                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "CurrentMediaDuration", extractElement("MediaDuration", tmp, ""), PARENT_DEVICE) 
     151                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "AVTransportURI", extractElement("CurrentURI", tmp, ""), PARENT_DEVICE) 
     152                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "NextAVTransportURI", extractElement("NextURI", tmp, ""), PARENT_DEVICE) 
     153                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "PlaybackStorageMedium", extractElement("PlayMedium", tmp, ""), PARENT_DEVICE) 
     154                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "RecordStorageMedium", extractElement("RecordMedium", tmp, ""), PARENT_DEVICE) 
     155                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "RecordMediumWriteStatus", extractElement("WriteStatus", tmp, ""), PARENT_DEVICE) 
     156 
     157    -- Get Current URI - song or radio station etc 
     158    status, tmp = upnp.UPnP_request(ip, ipPort, UPNP_AVTRANSPORT_URL, 
     159      "GetPositionInfo", upnp.UPNP_AVTRANSPORT_SERVICE, {InstanceID="0", Track="0", TrackMetaData="", TrackURI="", RelTime="", AbsTime="", RelCount="0", AbsCount="0"}) 
     160    changed = changed or setIfChanged("urn:schemas-micasaverde-com:device:avmisc:1", "TrackURI", extractElement("TrackURI", tmp, ""), PARENT_DEVICE) 
     161                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "CurrentTrack", extractElement("Track", tmp, ""), PARENT_DEVICE) 
     162                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "CurrentTrackDuration", extractElement("TrackDuration", tmp, ""), PARENT_DEVICE) 
     163                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "CurrentTrackURI", extractElement("TrackURI", tmp, ""), PARENT_DEVICE) 
     164                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "RelativeTimePosition", extractElement("RelTime", tmp, ""), PARENT_DEVICE) 
     165                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "AbsoluteTimePosition", extractElement("AbsTime", tmp, ""), PARENT_DEVICE) 
     166                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "RelativeCounterPosition", extractElement("RelTime", tmp, ""), PARENT_DEVICE) 
     167                      or setIfChanged(upnp.UPNP_AVTRANSPORT_SID, "AbsoluteCounterPosition", extractElement("AbsTime", tmp, ""), PARENT_DEVICE) 
     168 
     169    -- Get Mute status 
     170    status, tmp = upnp.UPnP_request(ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
     171      "GetMute", upnp.UPNP_RENDERING_CONTROL_SERVICE, {InstanceID="0", Channel="Master", CurrentMute=""}) 
     172    changed = changed or setIfChanged("urn:schemas-micasaverde-com:device:avmisc:1", "CurrentMute", extractElement("CurrentMute", tmp, ""), PARENT_DEVICE) 
     173                      or setIfChanged(upnp.UPNP_RENDERING_CONTROL_SID, "Mute", extractElement("CurrentMute", tmp, ""), PARENT_DEVICE) 
     174 
     175    -- Get Volume 
     176    status, tmp = upnp.UPnP_request(ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
     177      "GetVolume", upnp.UPNP_RENDERING_CONTROL_SERVICE, {InstanceID="0", Channel="Master", CurrentVolume=""}) 
     178    changed = changed or setIfChanged("urn:schemas-micasaverde-com:device:avmisc:1", "CurrentVolume", extractElement("CurrentVolume", tmp, ""), PARENT_DEVICE) 
     179                      or setIfChanged(upnp.UPNP_RENDERING_CONTROL_SID, "Volume", extractElement("CurrentVolume", tmp, ""), PARENT_DEVICE) 
     180 
     181    if (changed) then 
     182        luup.variable_set(HADEVICE_SID, "LastUpdate", os.time(), PARENT_DEVICE) 
     183    end 
     184  end 
     185 
     186  function deferredStartup() 
     187    debug("deferredStartup: start") 
     188 
     189    local status, tmp 
     190 
     191    local ip = luup.devices[PARENT_DEVICE].ip 
     192 
     193    -- 
     194    --  Attempt to subscribe to events 
     195    -- 
     196    --ip, port, event url, user-agent, callback, nt, TimeOut 
     197    -- status = upnp.UPnP_subscribe(ip, ipPort, UPNP_RENDERING_EVENT_URL, "http://192.168.5.209:3480/cmh/event") 
     198 
     199    refreshCache() 
     200  end 
     201 
    40202  function sonosStartup(lul_device) 
    41203    if (package.path:find("/etc/cmh-ludl/?.lua;/etc/cmh-lu/?.lua", 1, true) == nil) then 
     
    44206    package.loaded.L_Sonos1 = nil 
    45207 
     208    log("#" .. lul_device .. " starting up with id " .. luup.devices[lul_device].id) 
     209    PARENT_DEVICE = lul_device 
    46210    upnp = require("L_Sonos1") 
    47211    upnp.initialize(debug) 
    48      
    49     -- 
    50     --  Attempt to subscribe to events 
    51     -- 
    52     --ip, port, event url, user-agent, callback, nt, TimeOut 
    53     -- local l_SubscribeInfo = upnp.UPnP_subscribe(luup.devices[lul_device].ip, ipPort) 
    54          
    55     --  
    56     -- The next section reads various status on the Sonos Zone and populates variables on the device. For use by UI or Mute function, etc. 
    57     -- Is only executed upon initial load of module for now though 
    58     -- Get ZoneName 
    59     local l_DeviceInfo = upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_DEVICE_PROPERTIES_URL, 
    60                         "GetZoneAttributes", upnp.UPNP_DEVICE_PROPERTIES_SID, 
    61                         {CurrentZoneName="", CurrentIcon=""}) 
    62     l_CurrentZoneName = extractElement("CurrentZoneName", l_DeviceInfo, "")  
    63     luup.variable_set("urn:schemas-micasaverde-com:device:avmisc:1", "ZoneName", l_CurrentZoneName, lul_device) 
    64      
    65     -- GetCurrentTransportState  (PLAYING, STOPPED, etc) 
    66     local l_GetCurrentTransportState = upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    67                         "GetTransportInfo", upnp.UPNP_AVTRANSPORT_SID, 
    68                         {InstanceID="0", CurrentTransportState="", CurrentTransportStatus="", CurrentSpeed = "1"}) 
    69     l_CurrentTransportState = extractElement("CurrentTransportState", l_GetCurrentTransportState, "")    
    70     luup.variable_set("urn:schemas-micasaverde-com:device:avmisc:1", "CurrentTransportState", l_CurrentTransportState,  lul_device) 
    71          
    72     -- Get Playmode (NORMAL, REPEAT_ALL, SHUFFLE_NOREPEAT, SHUFFLE) 
    73     local l_PlayMode = upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    74                         "GetTransportSettings", upnp.UPNP_AVTRANSPORT_SID, 
    75                         {InstanceID="0", PlayMode="", ReqQualityMode=""}) 
    76     l_PlayMode = extractElement("PlayMode", l_PlayMode, "")  
    77     luup.variable_set("urn:schemas-micasaverde-com:device:avmisc:1", "PlayMode", l_PlayMode, lul_device) 
    78          
    79     -- Get Current URI - song or radio station etc 
    80     local l_PositionInfo = upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    81                         "GetPositionInfo", upnp.UPNP_AVTRANSPORT_SID, 
    82                         {InstanceID="0", Track="0", TrackMetaData="", TrackURI = "", RelTime = "", AbsTime = "", RelCount = "0", AbsCount = "0"}) 
    83     l_TrackURI = extractElement("TrackURI", l_PositionInfo, "")  
    84     luup.variable_set("urn:schemas-micasaverde-com:device:avmisc:1", "TrackURI", l_TrackURI, lul_device) 
    85  
    86     -- Get Mute status 
    87     local l_CurrentMute = upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
    88                         "GetMute", upnp.UPNP_RENDERING_CONTROL_SID, 
    89                         {InstanceID="0", Channel="Master", CurrentMute=""}) 
    90     l_CurrentMute = extractElement("CurrentMute", l_CurrentMute, "")     
    91     luup.variable_set("urn:schemas-micasaverde-com:device:avmisc:1", "CurrentMute", l_CurrentMute, lul_device) 
    92     isMuted = l_CurrentMute 
    93      
    94     -- Get Volume 
    95     local l_CurrentVolume = upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
    96                         "GetVolume", upnp.UPNP_RENDERING_CONTROL_SID, 
    97                         {InstanceID="0", Channel="Master", CurrentVolume=""}) 
    98     l_CurrentVolume = extractElement("CurrentVolume", l_CurrentVolume, "")   
    99     luup.variable_set("urn:schemas-micasaverde-com:device:avmisc:1", "CurrentVolume", l_CurrentVolume, lul_device) 
    100      
    101 end 
    102   
     212 
     213    luup.call_delay("deferredStartup", 1)  
     214  end 
    103215</functions> 
    104216<startup>sonosStartup</startup> 
     
    109221    <run> 
    110222      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    111                    "Play", upnp.UPNP_AVTRANSPORT_SID, 
    112                    {InstanceID="0", Speed="1"}) 
     223        "Play", upnp.UPNP_AVTRANSPORT_SERVICE, 
     224        {InstanceID="0", Speed="1"}) 
     225    </run> 
     226  </action> 
     227 
     228  <action> 
     229    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     230    <name>Play</name> 
     231    <run> 
     232      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     233        "Play", upnp.UPNP_AVTRANSPORT_SERVICE, 
     234        {InstanceID=(lul_settings.InstanceID or "0"), Speed=(lul_settings.Speed or "1")}) 
     235    </run> 
     236  </action> 
     237 
     238  <action> 
     239    <serviceId>urn:micasaverde-com:serviceId:Sonos1</serviceId> 
     240    <name>Say</name> 
     241    <run> 
     242      local url = string.format("x-rincon-mp3radio://translate.google.com/translate_tts?tl=%s&amp;q=%s", 
     243                                (lul_settings.Language or "en"), 
     244                                (url.escape(lul_settings.Text or "Please pass a text parameter"))), 
     245 
     246      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     247        "SetPlayMode", upnp.UPNP_AVTRANSPORT_SERVICE, 
     248        {InstanceID=(lul_settings.InstanceID or "0"), NewPlayMode="SHUFFLE_NOREPEAT"}) 
     249      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     250        "StartAutoplay", upnp.UPNP_AVTRANSPORT_SERVICE, 
     251        {InstanceID=(lul_settings.InstanceID or '0'), 
     252         ProgramURI=url, 
     253         ProgramMetaData="", 
     254         Volume=(lul_settings.Volume or 50), 
     255         IncludeLinkedZones=1, 
     256         ResetVolumeAfter=1}) 
     257    </run> 
     258  </action> 
     259 
     260  <action> 
     261    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     262    <name>Record</name> 
     263    <run> 
     264      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     265        "Record", upnp.UPNP_AVTRANSPORT_SERVICE, 
     266        {InstanceID=(lul_settings.InstanceID or "0")}) 
     267    </run> 
     268  </action> 
     269 
     270  <action> 
     271    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     272    <name>Seek</name> 
     273    <run> 
     274      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     275        "Record", upnp.UPNP_AVTRANSPORT_SERVICE, 
     276        {InstanceID=(lul_settings.InstanceID or "0"), Unit=(lul_settings.Unit or ""), Target=(lul_settings.Target or "")}) 
    113277    </run> 
    114278  </action> 
     
    119283    <run> 
    120284      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    121                    "Pause", upnp.UPNP_AVTRANSPORT_SID, 
    122                    {InstanceID="0"}) 
    123     </run> 
    124   </action> 
     285        "Pause", upnp.UPNP_AVTRANSPORT_SERVICE, 
     286        {InstanceID="0"}) 
     287    </run> 
     288  </action> 
     289 
     290  <action> 
     291    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     292    <name>Pause</name> 
     293    <run> 
     294      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     295        "Pause", upnp.UPNP_AVTRANSPORT_SERVICE, 
     296        {InstanceID=(lul_settings.InstanceID or "0")}) 
     297    </run> 
     298  </action> 
     299 
    125300 
    126301  <action> 
     
    129304    <run> 
    130305      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    131                    "Stop", upnp.UPNP_AVTRANSPORT_SID, 
    132                    {InstanceID="0"}) 
     306        "Stop", upnp.UPNP_AVTRANSPORT_SERVICE, 
     307        {InstanceID="0"}) 
     308    </run> 
     309  </action> 
     310 
     311  <action> 
     312    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     313    <name>Stop</name> 
     314    <run> 
     315      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     316        "Stop", upnp.UPNP_AVTRANSPORT_SERVICE, 
     317        {InstanceID=(lul_settings.InstanceID or "0")}) 
    133318    </run> 
    134319  </action> 
     
    139324    <run> 
    140325      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    141                    "Next", upnp.UPNP_AVTRANSPORT_SID, 
    142                    {InstanceID="0"}) 
    143  
     326        "Next", upnp.UPNP_AVTRANSPORT_SERVICE, 
     327        {InstanceID="0"}) 
     328    </run> 
     329  </action> 
     330 
     331  <action> 
     332    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     333    <name>Next</name> 
     334    <run> 
     335      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     336        "Next", upnp.UPNP_AVTRANSPORT_SERVICE, 
     337        {InstanceID=(lul_settings.InstanceID or "0")}) 
     338    </run> 
     339  </action> 
     340 
     341  <action> 
     342    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     343    <name>NextSection</name> 
     344    <run> 
     345      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     346        "NextSection", upnp.UPNP_AVTRANSPORT_SERVICE, 
     347        {InstanceID=(lul_settings.InstanceID or "0")}) 
    144348    </run> 
    145349  </action> 
     
    150354    <run> 
    151355      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    152                    "Previous", upnp.UPNP_AVTRANSPORT_SID, 
    153                    {InstanceID="0"}) 
    154  
     356        "Previous", upnp.UPNP_AVTRANSPORT_SERVICE, 
     357        {InstanceID="0"}) 
     358    </run> 
     359  </action> 
     360 
     361  <action> 
     362    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     363    <name>Previous</name> 
     364    <run> 
     365      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     366        "Previous", upnp.UPNP_AVTRANSPORT_SERVICE, 
     367        {InstanceID=(lul_settings.InstanceID or "0")}) 
     368    </run> 
     369  </action> 
     370 
     371  <action> 
     372    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     373    <name>PreviousSection</name> 
     374    <run> 
     375      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     376        "PreviousSection", upnp.UPNP_AVTRANSPORT_SERVICE, 
     377        {InstanceID=(lul_settings.InstanceID or "0")}) 
     378    </run> 
     379  </action> 
     380 
     381  <action> 
     382    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     383    <name>SetPlayMode</name> 
     384    <run> 
     385      -- NORMAL, SHUFFLE, SHUFFLE_NOREPEAT, REPEAT_ONE, REPEAT_ALL, RANDOM, DIRECT_1, INTRO 
     386      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     387        "SetPlayMode", upnp.UPNP_AVTRANSPORT_SERVICE, 
     388        {InstanceID=(lul_settings.InstanceID or "0"), NewPlayMode=(lul_settings.NewPlayMode)}) 
     389    </run> 
     390  </action> 
     391 
     392  <action> 
     393    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     394    <name>SetRecordQualityMode</name> 
     395    <run> 
     396      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     397        "SetRecordQualityMode", upnp.UPNP_AVTRANSPORT_SERVICE, 
     398        {InstanceID=(lul_settings.InstanceID or "0"), NewRecordQualityMode=(lul_settings.NewRecordQualityMode)}) 
     399    </run> 
     400  </action> 
     401 
     402  <action> 
     403    <serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId> 
     404    <name>SetMute</name> 
     405    <run> 
     406      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
     407        "SetMute", upnp.UPNP_RENDERING_CONTROL_SERVICE, 
     408        {InstanceID=(lul_settings.InstanceID or "0"), Channel=(lul_settings.Channel or "Master"), DesiredMute=(tonumber(lul_settings.DesiredMute) or 1)}) 
    155409    </run> 
    156410  </action> 
     
    161415    <run> 
    162416      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
    163                    "SetMute", upnp.UPNP_RENDERING_CONTROL_SID, 
    164                    {InstanceID="0", Channel="Master", DesiredMute=1}) 
     417        "SetMute", upnp.UPNP_RENDERING_CONTROL_SERVICE, 
     418        {InstanceID="0", Channel="Master", DesiredMute=1}) 
    165419    </run> 
    166420  </action> 
     
    169423    <serviceId>urn:micasaverde-com:serviceId:Sonos1</serviceId> 
    170424    <name>UnMute</name> 
    171         <run> 
    172       upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
    173                    "SetMute", upnp.UPNP_RENDERING_CONTROL_SID, 
    174                    {InstanceID="0", Channel="Master", DesiredMute=0}) 
    175     </run> 
    176   </action> 
    177    
    178    <action> 
     425    <run> 
     426      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
     427        "SetMute", upnp.UPNP_RENDERING_CONTROL_SERVICE, 
     428        {InstanceID="0", Channel="Master", DesiredMute=0}) 
     429    </run> 
     430  </action> 
     431 
     432  <action> 
     433    <serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId> 
     434    <name>SetVolume</name> 
     435    <run> 
     436      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
     437        "SetVolume", upnp.UPNP_RENDERING_CONTROL_SERVICE, 
     438        {InstanceID=(lul_settings.InstanceID or '0'), Channel=(lul.settings.Channel or 'Master'), DesiredVolume=(tonumber(lul_settings.DesiredVolume) or 0)}) 
     439    </run> 
     440  </action> 
     441 
     442  <action> 
    179443    <serviceId>urn:micasaverde-com:serviceId:Sonos1</serviceId> 
    180444    <name>SetVolume</name> 
    181445    <run> 
    182     upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
    183                    "SetVolume", upnp.UPNP_RENDERING_CONTROL_SID, 
    184                    {InstanceID='0', Channel='Master', DesiredVolume=tonumber(lul_settings.NewVolume)}) 
     446      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
     447        "SetVolume", upnp.UPNP_RENDERING_CONTROL_SERVICE, 
     448        {InstanceID='0', Channel='Master', DesiredVolume=tonumber(lul_settings.NewVolume)}) 
    185449    </run> 
    186450  </action> 
     
    190454    <name>SetFileToPlay</name> 
    191455    <run> 
    192     -- kept for backward compatability to first version. SetURIToPlay should be used instead. Will only allow protocol for files. 
    193     upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    194                    "SetAVTransportURI", upnp.UPNP_AVTRANSPORT_SID, 
    195                    {InstanceID='0', CurrentURI="x-file-cifs:" .. lul_settings.FileToPlay, CurrentURIMetaData = ""}) 
     456      -- kept for backward compatability to first version. SetURIToPlay should be used instead. Will only allow protocol for files. 
     457      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     458        "SetAVTransportURI", upnp.UPNP_AVTRANSPORT_SERVICE, 
     459        {InstanceID='0', CurrentURI="x-file-cifs:" .. lul_settings.FileToPlay, CurrentURIMetaData = ""}) 
     460    </run> 
     461  </action> 
     462 
     463  <action> 
     464    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     465    <name>SetAVTransportURI</name> 
     466    <run> 
     467      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     468        "SetAVTransportURI", upnp.UPNP_AVTRANSPORT_SERVICE, 
     469        {InstanceID=(lul_settings.InstanceID or '0'), CurrentURI=(lul_settings.CurrentURI), CurrentURIMetaData=(lul_settings.CurrentURIMetaData or "")}) 
     470    </run> 
     471  </action> 
     472 
     473  <action> 
     474    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     475    <name>AddURIToQueue</name> 
     476    <run> 
     477      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     478        "SetAVTransportURI", upnp.UPNP_AVTRANSPORT_SERVICE, 
     479        {InstanceID=(lul_settings.InstanceID or '0'), 
     480         EnqueuedURI=(lul_settings.EnqueuedURI or ""), 
     481         EnqueuedURIMetaData=(lul_settings.EnqueuedURIMetaData or ""), 
     482         DesiredFirstTrackNumberEnqueued=(lul_settings.DesiredFirstTrackNumberEnqueued or 1), 
     483         EnqueueAsNext=(lul_settings.EnqueueAsNext or true)}) 
     484    </run> 
     485  </action> 
     486 
     487  <action> 
     488    <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId> 
     489    <name>RemoveAllTracksFromQueue</name> 
     490    <run> 
     491      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     492        "RemoveAllTracksFromQueue", upnp.UPNP_AVTRANSPORT_SERVICE, 
     493        {InstanceID=(lul_settings.InstanceID or '0'))}) 
    196494    </run> 
    197495  </action> 
     
    201499    <name>SetURIToPlay</name> 
    202500    <run> 
    203     upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
    204                    "SetAVTransportURI", upnp.UPNP_AVTRANSPORT_SID, 
    205                    {InstanceID='0', CurrentURI=lul_settings.URIToPlay, CurrentURIMetaData = ""}) 
    206                     
    207                     -- URI must include protocol as prefix. 
    208                     -- x-file-cifs: 
    209                     -- file: 
    210                     -- x-rincon: 
    211                     -- x-rincon-mp3radio: 
    212                     -- x-rincon-playlist: 
    213                     -- x-rincon-queue: 
    214                     -- x-rincon-stream: 
    215                     -- example is DR Jazz Radio: x-rincon-mp3radio://live-icy.gss.dr.dk:8000/Channel22_HQ.mp3 
    216      
    217     </run> 
    218   </action> 
    219    
     501      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_AVTRANSPORT_URL, 
     502        "SetAVTransportURI", upnp.UPNP_AVTRANSPORT_SERVICE, 
     503        {InstanceID='0', CurrentURI=lul_settings.URIToPlay, CurrentURIMetaData = ""}) 
     504 
     505      -- URI must include protocol as prefix. 
     506      -- x-file-cifs: 
     507      -- file: 
     508      -- x-rincon: 
     509      -- x-rincon-mp3radio: 
     510      -- x-rincon-playlist: 
     511      -- x-rincon-queue: 
     512      -- x-rincon-stream: 
     513      -- example is DR Jazz Radio: x-rincon-mp3radio://live-icy.gss.dr.dk:8000/Channel22_HQ.mp3 
     514    </run> 
     515  </action> 
     516 
    220517  <action> 
    221518    <serviceId>urn:micasaverde-com:serviceId:Volume1</serviceId> 
     
    223520    <run> 
    224521      -- Toggle Mute 
    225       upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
    226                    "SetMute", upnp.UPNP_RENDERING_CONTROL_SID, 
    227                    {InstanceID="0", Channel="Master", DesiredMute=isMuted}) 
    228  
    229       isMuted = (isMuted == false) 
     522      local changed = false 
     523      local isMuted = luup.variable_get(upnp.UPNP_RENDERING_CONTROL_SID, "CurrentMute", lul_device) 
     524      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
     525        "SetMute", upnp.UPNP_RENDERING_CONTROL_SID, {InstanceID="0", Channel="Master", DesiredMute=isMuted}) 
     526 
     527      changed = changed or setIfChanged("urn:schemas-micasaverde-com:device:avmisc:1", "CurrentMute", (1-isMuted), lul_device) 
     528                        or setIfChanged(upnp.UPNP_RENDERING_CONTROL_SERVICE, "CurrentMute", (1-isMuted), PARENT_DEVICE) 
     529 
     530      if (changed) then 
     531          luup.variable_set(HADEVICE_SID, "LastUpdate", os.time(), PARENT_DEVICE) 
     532      end 
    230533    </run> 
    231534  </action> 
     
    237540      -- Volume up 
    238541      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
    239                    "SetRelativeVolume", upnp.UPNP_RENDERING_CONTROL_SID, 
    240                    {InstanceID="0", Channel="Master", Adjustment=3}) 
    241  
     542        "SetRelativeVolume", upnp.UPNP_RENDERING_CONTROL_SERVICE, 
     543        {InstanceID="0", Channel="Master", Adjustment=3}) 
    242544    </run> 
    243545  </action> 
     
    249551      -- Volume down 
    250552      upnp.UPnP_request(luup.devices[lul_device].ip, ipPort, UPNP_RENDERING_CONTROL_URL, 
    251                    "SetRelativeVolume", upnp.UPNP_RENDERING_CONTROL_SID, 
    252                    {InstanceID="0", Channel="Master", Adjustment=-3}) 
     553        "SetRelativeVolume", upnp.UPNP_RENDERING_CONTROL_SERVICE, 
     554        {InstanceID="0", Channel="Master", Adjustment=-3}) 
    253555    </run> 
    254556  </action> 
    255557</actionList> 
    256558</implementation> 
    257  
Note: See TracChangeset for help on using the changeset viewer.