19 | | TODO |
| 33 | In this example, the plugin at MiOS deviceId ''deviceId'' is interested in knowing about changes to the UPnP variable ''UPnPVariable''. |
| 34 | |
| 35 | Sending the UPnP SUBSCRIBE request is outside the scope of this document. For example code, see the Belkin WeMo plugin function [http://code.mios.com/trac/mios_belkin-wemo/browser/L_WeMo1.lua#L327 subscribeToDevice()]. The callback URL must be <http://''vera-address'':2529/upnp/event>. |
| 36 | |
| 37 | After the '''SUBSCRIBE''' request succeeds, the plugin will have a subscription identifier (SID) ''sid'', and a duration in seconds ''timeout''. For the next ''timeout'' seconds, the UPnP device will send '''NOTIFY''' messages to the callback URL. |
| 38 | |
| 39 | The plugin has an action ''VariableChanged'' in service ''pluginServiceId'' which accepts two parameters: ''valueParam'' (the new value of the varable), and ''sidParam'' (the subscription identifier). The plugin wants this action to be invoked by the proxy every time that ''UPnPVariable'' changes. |
| 40 | |
| 41 | To register the subscription with the proxy, perform an HTTP '''PUT''' to /upnp/event/''sid'': |
| 42 | |
| 43 | {{{ |
| 44 | local proxyRequestBody = "<subscription expiry='" .. os.time() + timeout .. "'>" |
| 45 | proxyRequestBody = proxyRequestBody .. |
| 46 | "<variable name='UPnPVariable' host='localhost' deviceId='" .. |
| 47 | deviceId .. "' serviceId='pluginServiceId' " .. |
| 48 | "action='VariableChanged' parameter='valueParam' sidParameter='sidParam'/>" |
| 49 | proxyRequestBody = proxyRequestBody .. "</subscription>" |
| 50 | local request, code = http.request({ |
| 51 | url = "http://localhost:2529/upnp/event/" .. url.escape(sid), |
| 52 | create = socketWithTimeout(2), |
| 53 | method = "PUT", |
| 54 | headers = { |
| 55 | ["Content-Type"] = "text/html", |
| 56 | ["Content-Length"] = proxyRequestBody:len(), |
| 57 | }, |
| 58 | source = ltn12.source.string(proxyRequestBody), |
| 59 | sink = ltn12.sink.null(), |
| 60 | }) |
| 61 | if (request == nil and code ~= "closed") then |
| 62 | -- Failed (timeout?) |
| 63 | elseif (code ~= 200) then |
| 64 | -- Failed (refused) |
| 65 | else |
| 66 | -- Succeeded |
| 67 | end |
| 68 | }}} |
| 69 | |
| 70 | The proxy will soon receive (or may have already received) the initial UPnP '''NOTIFY''' (with sequence 0), and will invoke the ''VariableChanged'' action on the plugin. |
31 | | TODO |
| 96 | In most cases, the plugin will want to keep renewing the subscription indefinitely. If the plugin knows that a subscription should be cancelled, it can ask the proxy to stop forwarding the event to the plugin with an HTTP '''DELETE''': |
| 97 | |
| 98 | {{{ |
| 99 | local request, code = http.request({ |
| 100 | url = "http://localhost:2529/upnp/event/" .. url.escape(sid), |
| 101 | create = socketWithTimeout(2), |
| 102 | method = "DELETE", |
| 103 | headers = { |
| 104 | ["Content-Length"] = 0, |
| 105 | }, |
| 106 | source = ltn12.source.empty(), |
| 107 | sink = ltn12.sink.null(), |
| 108 | }) |
| 109 | if (request == nil and code ~= "closed") then |
| 110 | -- Failed (timeout?) |
| 111 | elseif (code ~= 200) then |
| 112 | -- Failed (refused) |
| 113 | else |
| 114 | -- Succeeded |
| 115 | end |
| 116 | }}} |
| 117 | |
| 118 | If the Luup engine is restarted, the plugin will not have an opportunity to cancel a subscription, and the proxy will send events that were registered before the Luup engine was restarted. The plugin can use the ''sidParam'' value to see if this has happened. Eventually the subscription will expire and the plugin will stop receive notifications. |