diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index 8cc2fe9..d3381f9 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -30,6 +30,7 @@ definition( preferences { page(name:"mainPage", title:"Hue Device Setup", content:"mainPage", refreshTimeout:5) page(name:"bridgeDiscovery", title:"Hue Bridge Discovery", content:"bridgeDiscovery", refreshTimeout:5) + page(name:"bridgeDiscoveryFailed", title:"Bridge Discovery Failed", content:"bridgeDiscoveryFailed", refreshTimeout:0) page(name:"bridgeBtnPush", title:"Linking with your Hue", content:"bridgeLinking", refreshTimeout:5) page(name:"bulbDiscovery", title:"Hue Device Setup", content:"bulbDiscovery", refreshTimeout:5) } @@ -53,12 +54,21 @@ def bridgeDiscovery(params=[:]) def options = bridges ?: [] def numFound = options.size() ?: 0 - if (numFound == 0 && state.bridgeRefreshCount > 25) { - log.trace "Cleaning old bridges memory" - state.bridges = [:] - state.bridgeRefreshCount = 0 - app.updateSetting("selectedHue", "") - } + if (numFound == 0) { + if (state.bridgeRefreshCount == 25) { + log.trace "Cleaning old bridges memory" + state.bridges = [:] + app.updateSetting("selectedHue", "") + } else if (state.bridgeRefreshCount > 100) { + // five minutes have passed, give up + // there seems to be a problem going back from discovey failed page in some instances (compared to pressing next) + // however it is probably a SmartThings settings issue + state.bridges = [:] + app.updateSetting("selectedHue", "") + state.bridgeRefreshCount = 0 + return bridgeDiscoveryFailed() + } + } ssdpSubscribe() @@ -79,6 +89,13 @@ def bridgeDiscovery(params=[:]) } } +def bridgeDiscoveryFailed() { + return dynamicPage(name:"bridgeDiscoveryFailed", title: "Bridge Discovery Failed", nextPage: "bridgeDiscovery") { + section("Failed to discover any Hue Bridges. Please confirm that the Hue Bridge is connected to the same network as your SmartThings Hub, and that it has power.") { + } + } +} + def bridgeLinking() { int linkRefreshcount = !state.linkRefreshcount ? 0 : state.linkRefreshcount as int @@ -88,19 +105,15 @@ def bridgeLinking() def nextPage = "" def title = "Linking with your Hue" def paragraphText - def hueimage = null if (selectedHue) { paragraphText = "Press the button on your Hue Bridge to setup a link. " - hueimage = "http://huedisco.mediavibe.nl/wp-content/uploads/2013/09/pair-bridge.png" } else { paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next." - hueimage = null } if (state.username) { //if discovery worked nextPage = "bulbDiscovery" title = "Success!" paragraphText = "Linking to your hub was a success! Please click 'Next'!" - hueimage = null } if((linkRefreshcount % 2) == 0 && !state.username) { @@ -110,8 +123,6 @@ def bridgeLinking() return dynamicPage(name:"bridgeBtnPush", title:title, nextPage:nextPage, refreshInterval:refreshInterval) { section("") { paragraph """${paragraphText}""" - if (hueimage != null) - image "${hueimage}" } } } @@ -135,13 +146,14 @@ def bulbDiscovery() { if((bulbRefreshCount % 5) == 0) { discoverHueBulbs() } + def selectedBridge = state.bridges.find { key, value -> value?.serialNumber?.equalsIgnoreCase(selectedHue) } + def title = selectedBridge?.value?.name ?: "Find bridges" return dynamicPage(name:"bulbDiscovery", title:"Bulb Discovery Started!", nextPage:"", refreshInterval:refreshInterval, install:true, uninstall: true) { section("Please wait while we discover your Hue Bulbs. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") { input "selectedBulbs", "enum", required:false, title:"Select Hue Bulbs (${numFound} found)", multiple:true, options:bulboptions } section { - def title = getBridgeIP() ? "Hue bridge (${getBridgeIP()})" : "Find bridges" href "bridgeDiscovery", title: title, description: "", state: selectedHue ? "complete" : "incomplete", params: [override: true] } @@ -348,18 +360,19 @@ def addBulbs() { def newHueBulb if (bulbs instanceof java.util.Map) { newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni } - if (newHueBulb != null) { - d = addChildBulb(dni, newHueBulb?.value?.type, newHueBulb?.value?.name, newHueBulb?.value?.hub) + + if (newHueBulb != null) { + d = addChildBulb(dni, newHueBulb?.value?.type, newHueBulb?.value?.name, newHueBulb?.value?.hub) if (d) { log.debug "created ${d.displayName} with id $dni" d.completedSetup = true d.refresh() } - } else { - log.debug "$dni in not longer paired to the Hue Bridge or ID changed" - } + } else { + log.debug "$dni in not longer paired to the Hue Bridge or ID changed" + } } else { - //backwards compatable + //backwards compatable newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni } d = addChildBulb(dni, "Extended Color Light", newHueBulb?.value?.name, newHueBulb?.value?.hub) d?.completedSetup = true @@ -369,7 +382,7 @@ def addBulbs() { log.debug "found ${d.displayName} with id $dni already exists, type: '$d.typeName'" if (bulbs instanceof java.util.Map) { // Update device type if incorrect - def newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni } + def newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni } upgradeDeviceType(d, newHueBulb?.value?.type) } } @@ -489,7 +502,21 @@ void bridgeDescriptionHandler(physicalgraph.device.HubResponse hubResponse) { def bridges = getHueBridges() def bridge = bridges.find {it?.key?.contains(body?.device?.UDN?.text())} if (bridge) { - bridge.value << [name:body?.device?.friendlyName?.text(), serialNumber:body?.device?.serialNumber?.text(), verified: true] + // serialNumber from API is in format of 0017882413ad (mac address), however on the actual bridge only last six + // characters are printed on the back so using that to identify bridge + def idNumber = body?.device?.serialNumber?.text() + if (idNumber?.size() >= 6) + idNumber = idNumber[-6..-1].toUpperCase() + + // usually in form of bridge name followed by (ip), i.e. defaults to Philips Hue (192.168.1.2) + // replace IP with serial number to make it easier for user to identify + def name = body?.device?.friendlyName?.text() + def index = name?.indexOf('(') + if (index != -1) { + name = name.substring(0,index) + name += " ($idNumber)" + } + bridge.value << [name:name, serialNumber:body?.device?.serialNumber?.text(), verified: true] } else { log.error "/description.xml returned a bridge that didn't exist" }