Update beacon-control.groovy
[smartapps.git] / official / simple-sync-connect.groovy
1 /**
2  *  Simple Sync Connect
3  *
4  *  Copyright 2015 Roomie Remote, Inc.
5  *
6  *      Date: 2015-09-22
7  */
8
9 definition(
10     name: "Simple Sync Connect",
11     namespace: "roomieremote-raconnect",
12     author: "Roomie Remote, Inc.",
13     description: "Integrate SmartThings with your Simple Control activities via Simple Sync.",
14     category: "My Apps",
15     iconUrl: "https://s3.amazonaws.com/roomieuser/remotes/simplesync-60.png",
16     iconX2Url: "https://s3.amazonaws.com/roomieuser/remotes/simplesync-120.png",
17     iconX3Url: "https://s3.amazonaws.com/roomieuser/remotes/simplesync-120.png")
18
19 preferences()
20 {
21         page(name: "mainPage", title: "Simple Sync Setup", content: "mainPage", refreshTimeout: 5)
22     page(name:"agentDiscovery", title:"Simple Sync Discovery", content:"agentDiscovery", refreshTimeout:5)
23     page(name:"manualAgentEntry")
24     page(name:"verifyManualEntry")
25 }
26
27 def mainPage()
28 {
29         if (canInstallLabs())
30     {
31         return agentDiscovery()
32     }
33     else
34     {
35         def upgradeNeeded = """To use SmartThings Labs, your Hub should be completely up to date.
36
37 To update your Hub, access Location Settings in the Main Menu (tap the gear next to your location name), select your Hub, and choose "Update Hub"."""
38
39         return dynamicPage(name:"mainPage", title:"Upgrade needed!", nextPage:"", install:false, uninstall: true) {
40             section("Upgrade")
41             {
42                 paragraph "$upgradeNeeded"
43             }
44         }
45     }
46 }
47
48 def agentDiscovery(params=[:])
49 {
50         int refreshCount = !state.refreshCount ? 0 : state.refreshCount as int
51     state.refreshCount = refreshCount + 1
52     def refreshInterval = refreshCount == 0 ? 2 : 5
53         
54     if (!state.subscribe)
55     {
56         subscribe(location, null, locationHandler, [filterEvents:false])
57         state.subscribe = true
58     }
59         
60     //ssdp request every fifth refresh
61     if ((refreshCount % 5) == 0)
62     {
63         discoverAgents()
64     }
65         
66     def agentsDiscovered = agentsDiscovered()
67     
68     return dynamicPage(name:"agentDiscovery", title:"Pair with Simple Sync", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: true) {
69         section("Pair with Simple Sync")
70         {
71             input "selectedAgent", "enum", required:true, title:"Select Simple Sync\n(${agentsDiscovered.size() ?: 0} found)", multiple:false, options:agentsDiscovered
72                 href(name:"manualAgentEntry",
73                  title:"Manually Configure Simple Sync",
74                  required:false,
75                  page:"manualAgentEntry")
76         }
77     }
78 }
79
80 def manualAgentEntry()
81 {
82         dynamicPage(name:"manualAgentEntry", title:"Manually Configure Simple Sync", nextPage:"verifyManualEntry", install:false, uninstall:true) {
83         section("Manually Configure Simple Sync")
84         {
85                 paragraph "In the event that Simple Sync cannot be automatically discovered by your SmartThings hub, you may enter Simple Sync's IP address here."
86             input(name: "manualIPAddress", type: "text", title: "IP Address", required: true)
87         }
88     }
89 }
90
91 def verifyManualEntry()
92 {
93     def hexIP = convertIPToHexString(manualIPAddress)
94     def hexPort = convertToHexString(47147)
95     def uuid = "593C03D2-1DA9-4CDB-A335-6C6DC98E56C3"
96     def hubId = ""
97     
98     for (hub in location.hubs)
99     {
100         if (hub.localIP != null)
101         {
102                 hubId = hub.id
103             break
104         }
105     }
106     
107     def manualAgent = [deviceType: "04",
108                                         mac: "unknown",
109                                         ip: hexIP,
110                         port: hexPort,
111                         ssdpPath: "/upnp/Roomie.xml",
112                         ssdpUSN: "uuid:$uuid::urn:roomieremote-com:device:roomie:1",
113                         hub: hubId,
114                         verified: true,
115                         name: "Simple Sync $manualIPAddress"]
116         
117     state.agents[uuid] = manualAgent
118     
119     addOrUpdateAgent(state.agents[uuid])
120     
121     dynamicPage(name: "verifyManualEntry", title: "Manual Configuration Complete", nextPage: "", install:true, uninstall:true) {
122         section("")
123         {
124                 paragraph("Tap Done to complete the installation process.")
125         }
126     }
127 }
128
129 def discoverAgents()
130 {
131     def urn = getURN()
132     
133     sendHubCommand(new physicalgraph.device.HubAction("lan discovery $urn", physicalgraph.device.Protocol.LAN))
134 }
135
136 def agentsDiscovered()
137 {
138     def gAgents = getAgents()
139     def agents = gAgents.findAll { it?.value?.verified == true }
140     def map = [:]
141     agents.each
142     {
143         map["${it.value.uuid}"] = it.value.name
144     }
145     map
146 }
147
148 def getAgents()
149 {
150     if (!state.agents)
151     {
152         state.agents = [:]
153     }
154     
155     state.agents
156 }
157
158 def installed()
159 {
160         initialize()
161 }
162
163 def updated()
164 {
165         initialize()
166 }
167
168 def initialize()
169 {
170         if (state.subscribe)
171         {
172         unsubscribe()
173                 state.subscribe = false
174         }
175     
176     if (selectedAgent)
177     {
178         addOrUpdateAgent(state.agents[selectedAgent])
179     }
180 }
181
182 def addOrUpdateAgent(agent)
183 {
184         def children = getChildDevices()
185         def dni = agent.ip + ":" + agent.port
186     def found = false
187         
188         children.each
189         {
190                 if ((it.getDeviceDataByName("mac") == agent.mac))
191                 {
192                 found = true
193             
194             if (it.getDeviceNetworkId() != dni)
195             {
196                                 it.setDeviceNetworkId(dni)
197                         }
198                 }
199         else if (it.getDeviceNetworkId() == dni)
200         {
201                 found = true
202         }
203         }
204     
205         if (!found)
206         {
207         addChildDevice("roomieremote-agent", "Simple Sync", dni, agent.hub, [label: "Simple Sync"])
208         }
209 }
210
211 def locationHandler(evt)
212 {
213     def description = evt?.description
214     def urn = getURN()
215     def hub = evt?.hubId
216     def parsedEvent = parseEventMessage(description)
217     
218     parsedEvent?.putAt("hub", hub)
219     
220     //SSDP DISCOVERY EVENTS
221         if (parsedEvent?.ssdpTerm?.contains(urn))
222         {
223         def agent = parsedEvent
224         def ip = convertHexToIP(agent.ip)
225         def agents = getAgents()
226         
227         agent.verified = true
228         agent.name = "Simple Sync $ip"
229         
230         if (!agents[agent.uuid])
231         {
232                 state.agents[agent.uuid] = agent
233         }
234     }
235 }
236
237 private def parseEventMessage(String description)
238 {
239         def event = [:]
240         def parts = description.split(',')
241     
242         parts.each
243     { part ->
244                 part = part.trim()
245                 if (part.startsWith('devicetype:'))
246         {
247                         def valueString = part.split(":")[1].trim()
248                         event.devicetype = valueString
249                 }
250                 else if (part.startsWith('mac:'))
251         {
252                         def valueString = part.split(":")[1].trim()
253                         if (valueString)
254             {
255                                 event.mac = valueString
256                         }
257                 }
258                 else if (part.startsWith('networkAddress:'))
259         {
260                         def valueString = part.split(":")[1].trim()
261                         if (valueString)
262             {
263                                 event.ip = valueString
264                         }
265                 }
266                 else if (part.startsWith('deviceAddress:'))
267         {
268                         def valueString = part.split(":")[1].trim()
269                         if (valueString)
270             {
271                                 event.port = valueString
272                         }
273                 }
274                 else if (part.startsWith('ssdpPath:'))
275         {
276                         def valueString = part.split(":")[1].trim()
277                         if (valueString)
278             {
279                                 event.ssdpPath = valueString
280                         }
281                 }
282                 else if (part.startsWith('ssdpUSN:'))
283         {
284                         part -= "ssdpUSN:"
285                         def valueString = part.trim()
286                         if (valueString)
287             {
288                                 event.ssdpUSN = valueString
289                 
290                 def uuid = getUUIDFromUSN(valueString)
291                 
292                 if (uuid)
293                 {
294                         event.uuid = uuid
295                 }
296                         }
297                 }
298                 else if (part.startsWith('ssdpTerm:'))
299         {
300                         part -= "ssdpTerm:"
301                         def valueString = part.trim()
302                         if (valueString)
303             {
304                                 event.ssdpTerm = valueString
305                         }
306                 }
307                 else if (part.startsWith('headers'))
308         {
309                         part -= "headers:"
310                         def valueString = part.trim()
311                         if (valueString)
312             {
313                                 event.headers = valueString
314                         }
315                 }
316                 else if (part.startsWith('body'))
317         {
318                         part -= "body:"
319                         def valueString = part.trim()
320                         if (valueString)
321             {
322                                 event.body = valueString
323                         }
324                 }
325         }
326
327         event
328 }
329
330 def getURN()
331 {
332     return "urn:roomieremote-com:device:roomie:1"
333 }
334
335 def getUUIDFromUSN(usn)
336 {
337         def parts = usn.split(":")
338         
339         for (int i = 0; i < parts.size(); ++i)
340         {
341                 if (parts[i] == "uuid")
342                 {
343                         return parts[i + 1]
344                 }
345         }
346 }
347
348 def String convertHexToIP(hex)
349 {
350         [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
351 }
352
353 def Integer convertHexToInt(hex)
354 {
355         Integer.parseInt(hex,16)
356 }
357
358 def String convertToHexString(n)
359 {
360         String hex = String.format("%X", n.toInteger())
361 }
362
363 def String convertIPToHexString(ipString)
364 {
365         String hex = ipString.tokenize(".").collect {
366         String.format("%02X", it.toInteger())
367     }.join()
368 }
369
370 def Boolean canInstallLabs()
371 {
372     return hasAllHubsOver("000.011.00603")
373 }
374
375 def Boolean hasAllHubsOver(String desiredFirmware)
376 {
377     return realHubFirmwareVersions.every { fw -> fw >= desiredFirmware }
378 }
379
380 def List getRealHubFirmwareVersions()
381 {
382     return location.hubs*.firmwareVersionString.findAll { it }
383 }