UPNP Questions and Quandaries

childdevice

(Matt Brain) #1

Hi All

I have been beavering away developing a UPNP interface for my alarm, a Cytech Comfort system.

This alarm has a crude but effective and reliable serial interface which is also presented over TCP/IP via an adapter which can report back and allow control over all its elements. It has numerous general purpose inputs and outputs, and bunch of different user interfaces and third party device interfaces as well as its own scripting language allowing a home owner to programme home automation tasks.

I was going to write a dedicated interface for SmartThings but actually thought a UPNP interface would ultimately be more useful to the alarm user community and it offers a fairly nice discovery and eventing framework.

I have built the interface using node.js, and it runs very nicely on a RPi (for those of you considering this there are some pretty nice UPNP node modules available to make the task easier - but like lots of things node, they do require a bit of polishing to get them to work the way you want them to)

The UPNP interface presents all the alarm input, outputs and various other elements via a number of devices with related services on the RPi via a single port. This is supported by the UPNP spec and it permits a UPNP client to only subscribe to the devices and services it is interested in.

However, the SmartThings implementation (or at least the examples I have seen in both the documentation and in the wild) only allows one SmartThings device to have a relationship with a UPNP server (as restricted by IP:PORT) as it seems a device will only receive notifications to it when the MAC or IP:PORT is defined in its Device Network Id. As a device is uniquely identified by the Device Network Id, this means a ‘bridging or aggregating’ device is needed to receive incoming data from the UPNP server.

This brings up a number of issues I could do with some help on:

  1. Subscriptions on the local lan are asynchronous in the SmartThings implementation, you have to fire a [physicalgraph.device.HubAction(method: “SUBSCRIBE”,…] and receive the results in parse(). Annoyingly the response received is an UPNP subscribe ack which contains a SID but has no reference to the device or service. A device has a unique subURL for the purposes or creating, renewing or terminating subscriptions but the actual content of the response doesn’t indicate the source device or service of the ack. Therefore, knowing which subscription has been successful is pretty tricky, relying on getting event data back which does include enough information (SID) and a callback path which can be unique per UPNP device. If a subscription fails, it appears to fail silently and therefore it is hard to implement a retry (and indeed renewal mechanism). This could be overcome if the HubAction supported a callback allowing the subscribe and response to be associated with one another

  2. Similarly when receiving eventing data from previous (orphaned subscriptions) it is really hard to track them down and unsubscribe, relying on tricks to encode the requesting subURL in the callback. This becomes a problem when performing iterative development and lots of old subscriptions exist on the UPNP server

  3. As only a single device can receive data from the UPNP server, i currently parse the data in bridge devices, pass it back to the parent SmartApp and then forward it to the relevant child device which is representing the device in SmartThings - is there a more elegant way to do this?

  4. I keep losing packets - sometimes events or acks go astray - i can see them on the ‘wire’ but they seem to be dropped by the SmartHub. This only becomes evident in the device when i have another event hit it with an out of order SEQ number, but if i rely on incoming events to perform automation I may be missing triggers.

  5. I may run into rate limit issues on the bridge device, a typical Cytech Alarm may have 20+ zone inputs (there used to be a limit of 64 but I think it has now increased) and in a busy building it is very easy to have each PIR firing several times in a minute, combine that with other I/O, user interfaces, etc. it would be very easy to have more than 250 events in a minute through a single device

Questions:

Can i have a SmartApp manage the communications to the UPNP server and act as a bridge, this would overcome an issue i have with state (which tracks subscriptions) being accessed and updated incorrectly by using atomicstate (although not having maps introduces further complexity)

Is there a better way to manage subscriptions so i can track requests and responses?

Any ideas of a better architecture or relationship between the discovery SmartApp, bridge device and multiple end devices?

Any ideas why packets are being lost?

Why does the smartApp SSDP disovery process send out so many requests resulting in a flood of SSDP responses, each of which generates a request to get the device descriptor and therefore lots of duplicate responses (based on UPNP example smartApp)

Thanks,

Matt


(Michael) #2

You may want to see how other alarm panel integrations are implemented. Lots of options documented on this community. Alarm Decoder has a piece of hardware that acts as a keypad for Ademco and DCS alarm panels. They created an offering that is a RPi along with their AD2Pi that updates ST via web service. You may be able to learn from what they did to make yours work. Their code is publicly available here:
https://github.com/nutechsoftware


(Matt Brain) #3

Having looked at this further, I have made some changes to the UPNP implementation on the Cytech side and am now using a different port per device. This simplifies up the code significantly as now each device on SmartThings can talk directly with its peer on UPNP. Routing is performed by IP:PORT in the DeviceNetworkId.

The only issue is with unsolicited eventing coming from the alarm, these all have to get swallowed up by a single SmartThings device as they appear to be routed via mac address (and therefore require a device with the mac address as the DeviceNetworkId. Fortunately I am able to route from this device to the appropriate device via the SmartApp. Ideally, these events should be routed by SID or Callback URL directly to the related device but there doesn’t appear to be any way to do that or failing that if the SmartApp could receive these unsolicited events it would make it easier but I can’t figure out how to do any of this - any thoughts?