Hue fix bulb discovery and link button

This commit is contained in:
Juan Pablo Risso
2015-09-10 21:45:08 -04:00
parent 6005f7266e
commit d7490a086a

View File

@@ -15,7 +15,7 @@
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
*/ */
definition( definition(
name: "Hue (Connect)", name: "Hue (Connect)",
namespace: "smartthings", namespace: "smartthings",
@@ -64,10 +64,13 @@ def bridgeDiscovery(params=[:])
def options = bridges ?: [] def options = bridges ?: []
def numFound = options.size() ?: 0 def numFound = options.size() ?: 0
if(!state.subscribe) { if (numFound == 0 && state.bridgeRefreshCount > 25) {
subscribe(location, null, locationHandler, [filterEvents:false]) log.trace "Cleaning old bridges memory"
state.subscribe = true state.bridges = [:]
} state.bridgeRefreshCount = 0
}
subscribe(location, null, locationHandler, [filterEvents:false])
//bridge discovery request every 15 //25 seconds //bridge discovery request every 15 //25 seconds
if((bridgeRefreshCount % 5) == 0) { if((bridgeRefreshCount % 5) == 0) {
@@ -94,11 +97,20 @@ def bridgeLinking()
def nextPage = "" def nextPage = ""
def title = "Linking with your Hue" def title = "Linking with your Hue"
def paragraphText = "Press the button on your Hue Bridge to setup a link." 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 if (state.username) { //if discovery worked
nextPage = "bulbDiscovery" nextPage = "bulbDiscovery"
title = "Success! - click 'Next'" title = "Success!"
paragraphText = "Linking to your hub was a success! Please click 'Next'!" paragraphText = "Linking to your hub was a success! Please click 'Next'!"
hueimage = null
} }
if((linkRefreshcount % 2) == 0 && !state.username) { if((linkRefreshcount % 2) == 0 && !state.username) {
@@ -106,18 +118,20 @@ def bridgeLinking()
} }
return dynamicPage(name:"bridgeBtnPush", title:title, nextPage:nextPage, refreshInterval:refreshInterval) { return dynamicPage(name:"bridgeBtnPush", title:title, nextPage:nextPage, refreshInterval:refreshInterval) {
section("Button Press") { section("") {
paragraph """${paragraphText}""" paragraph """${paragraphText}"""
if (hueimage != null)
image "${hueimage}"
} }
} }
} }
def bulbDiscovery() def bulbDiscovery() {
{
int bulbRefreshCount = !state.bulbRefreshCount ? 0 : state.bulbRefreshCount as int int bulbRefreshCount = !state.bulbRefreshCount ? 0 : state.bulbRefreshCount as int
state.bulbRefreshCount = bulbRefreshCount + 1 state.bulbRefreshCount = bulbRefreshCount + 1
def refreshInterval = 3 def refreshInterval = 3
state.inBulbDiscovery = true
state.bridgeRefreshCount = 0
def options = bulbsDiscovered() ?: [] def options = bulbsDiscovered() ?: []
def numFound = options.size() ?: 0 def numFound = options.size() ?: 0
@@ -129,7 +143,7 @@ def bulbDiscovery()
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.") { 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:options input "selectedBulbs", "enum", required:false, title:"Select Hue Bulbs (${numFound} found)", multiple:true, options:options
} }
section { section {
def title = getBridgeIP() ? "Hue bridge (${getBridgeIP()})" : "Find bridges" def title = getBridgeIP() ? "Hue bridge (${getBridgeIP()})" : "Find bridges"
href "bridgeDiscovery", title: title, description: "", state: selectedHue ? "complete" : "incomplete", params: [override: true] href "bridgeDiscovery", title: title, description: "", state: selectedHue ? "complete" : "incomplete", params: [override: true]
@@ -194,21 +208,22 @@ Map bridgesDiscovered() {
Map bulbsDiscovered() { Map bulbsDiscovered() {
def bulbs = getHueBulbs() def bulbs = getHueBulbs()
def map = [:] def bulbmap = [:]
if (bulbs instanceof java.util.Map) { if (bulbs instanceof java.util.Map) {
bulbs.each { bulbs.each {
def value = "${it?.value?.name}" def value = "${it.value.name}"
def key = app.id +"/"+ it?.value?.id def key = app.id +"/"+ it.value.id
map["${key}"] = value bulbmap["${key}"] = value
} }
} else { //backwards compatable } else { //backwards compatable
bulbs.each { bulbs.each {
def value = "${it?.name}" def value = "${it.name}"
def key = app.id +"/"+ it?.id def key = app.id +"/"+ it.id
map["${key}"] = value logg += "$value - $key, "
bulbmap["${key}"] = value
} }
} }
map bulbmap
} }
def getHueBulbs() { def getHueBulbs() {
@@ -231,24 +246,16 @@ def installed() {
def updated() { def updated() {
log.trace "Updated with settings: ${settings}" log.trace "Updated with settings: ${settings}"
unschedule() unschedule()
unsubscribe() unsubscribe()
initialize() initialize()
} }
def initialize() { def initialize() {
log.debug "Initializing" log.debug "Initializing"
state.subscribe = false state.inBulbDiscovery = false
state.bridgeSelectedOverride = false
def bridge = null
if (selectedHue) { if (selectedHue) {
addBridge() addBridge()
bridge = getChildDevice(selectedHue) addBulbs()
subscribe(bridge, "bulbList", bulbListHandler)
}
if (selectedBulbs) {
addBulbs()
doDeviceSync() doDeviceSync()
runEvery5Minutes("doDeviceSync") runEvery5Minutes("doDeviceSync")
} }
@@ -263,22 +270,27 @@ def manualRefresh() {
def uninstalled(){ def uninstalled(){
state.bridges = [:] state.bridges = [:]
state.subscribe = false state.username = null
} }
// Handles events to add new bulbs // Handles events to add new bulbs
def bulbListHandler(evt) { def bulbListHandler(hub, data = "") {
def bulbs = [:] def msg = "Bulbs list not processed. Only while in settings menu."
log.trace "Adding bulbs to state..." log.trace "Here: $hub, $data"
state.bridgeProcessedLightList = true if (state.inBulbDiscovery) {
evt.jsonData.each { k,v -> def bulbs = [:]
log.trace "$k: $v" def logg = ""
if (v instanceof Map) { log.trace "Adding bulbs to state..."
bulbs[k] = [id: k, name: v.name, type: v.type, hub:evt.value] state.bridgeProcessedLightList = true
} def object = new groovy.json.JsonSlurper().parseText(data)
} object.each { k,v ->
state.bulbs = bulbs if (v instanceof Map)
log.info "${bulbs.size()} bulbs found" bulbs[k] = [id: k, name: v.name, type: v.type, hub:hub]
}
state.bulbs = bulbs
msg = "${bulbs.size()} bulbs found. $state.bulbs"
}
return msg
} }
def addBulbs() { def addBulbs() {
@@ -294,7 +306,7 @@ def addBulbs() {
} else { } else {
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name]) d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
} }
} else { } else {
//backwards compatable //backwards compatable
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni } newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name]) d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name])
@@ -322,7 +334,7 @@ def addBridge() {
def d = getChildDevice(selectedHue) def d = getChildDevice(selectedHue)
if(!d) { if(!d) {
// compatibility with old devices // compatibility with old devices
def newbridge = true def newbridge = true
childDevices.each { childDevices.each {
if (it.getDeviceDataByName("mac")) { if (it.getDeviceDataByName("mac")) {
def newDNI = "${it.getDeviceDataByName("mac")}" def newDNI = "${it.getDeviceDataByName("mac")}"
@@ -332,22 +344,27 @@ def addBridge() {
it.setDeviceNetworkId("${newDNI}") it.setDeviceNetworkId("${newDNI}")
if (oldDNI == selectedHue) if (oldDNI == selectedHue)
app.updateSetting("selectedHue", newDNI) app.updateSetting("selectedHue", newDNI)
newbridge = false newbridge = false
} }
} }
} }
if (newbridge) { if (newbridge) {
d = addChildDevice("smartthings", "Hue Bridge", selectedHue, vbridge.value.hub) d = addChildDevice("smartthings", "Hue Bridge", selectedHue, vbridge.value.hub)
log.debug "created ${d.displayName} with id ${d.deviceNetworkId}" log.debug "created ${d.displayName} with id ${d.deviceNetworkId}"
def childDevice = getChildDevice(d.deviceNetworkId) def childDevice = getChildDevice(d.deviceNetworkId)
childDevice.sendEvent(name: "serialNumber", value: vbridge.value.serialNumber) childDevice.sendEvent(name: "serialNumber", value: vbridge.value.serialNumber)
if (vbridge.value.ip && vbridge.value.port) { if (vbridge.value.ip && vbridge.value.port) {
if (vbridge.value.ip.contains(".")) if (vbridge.value.ip.contains(".")) {
childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port) childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port)
else childDevice.updateDataValue("networkAddress", vbridge.value.ip + ":" + vbridge.value.port)
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port)) } else {
} else childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
childDevice.updateDataValue("networkAddress", convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port))
}
} else {
childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress)) childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress))
childDevice.updateDataValue("networkAddress", convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress))
}
} }
} else { } else {
log.debug "found ${d.displayName} with id $selectedHue already exists" log.debug "found ${d.displayName} with id $selectedHue already exists"
@@ -355,7 +372,6 @@ def addBridge() {
} }
} }
def locationHandler(evt) { def locationHandler(evt) {
def description = evt.description def description = evt.description
log.trace "Location: $description" log.trace "Location: $description"
@@ -454,17 +470,13 @@ def locationHandler(evt) {
def doDeviceSync(){ def doDeviceSync(){
log.trace "Doing Hue Device Sync!" log.trace "Doing Hue Device Sync!"
//shrink the large bulb lists
convertBulbListToMap() convertBulbListToMap()
poll() poll()
try {
if(!state.subscribe) {
subscribe(location, null, locationHandler, [filterEvents:false]) subscribe(location, null, locationHandler, [filterEvents:false])
state.subscribe = true } catch (all) {
} log.trace "Subscription already exist"
}
discoverBridges() discoverBridges()
} }
@@ -473,44 +485,49 @@ def doDeviceSync(){
///////////////////////////////////// /////////////////////////////////////
def parse(childDevice, description) { def parse(childDevice, description) {
def parsedEvent = parseLanMessage(description) def parsedEvent = parseLanMessage(description)
if (parsedEvent.headers && parsedEvent.body) { if (parsedEvent.headers && parsedEvent.body) {
def headerString = parsedEvent.headers.toString() def headerString = parsedEvent.headers.toString()
if (headerString?.contains("json")) { def bodyString = parsedEvent.body.toString()
def body = new groovy.json.JsonSlurper().parseText(parsedEvent.body) if (headerString?.contains("json")) {
if (body instanceof java.util.HashMap) def body
{ //poll response try {
body = new groovy.json.JsonSlurper().parseText(bodyString)
} catch (all) {
log.warn "Parsing Body failed - trying again..."
poll()
}
if (body instanceof java.util.HashMap) {
//poll response
def bulbs = getChildDevices() def bulbs = getChildDevices()
//for each bulb
for (bulb in body) { for (bulb in body) {
def d = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"} def d = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"}
if (d) { if (d) {
if (bulb.value.state?.reachable) { if (bulb.value.state?.reachable) {
sendEvent(d.deviceNetworkId, [name: "switch", value: bulb.value?.state?.on ? "on" : "off"]) sendEvent(d.deviceNetworkId, [name: "switch", value: bulb.value?.state?.on ? "on" : "off"])
sendEvent(d.deviceNetworkId, [name: "level", value: Math.round(bulb.value.state.bri * 100 / 255)]) sendEvent(d.deviceNetworkId, [name: "level", value: Math.round(bulb.value.state.bri * 100 / 255)])
if (bulb.value.state.sat) { if (bulb.value.state.sat) {
def hue = Math.min(Math.round(bulb.value.state.hue * 100 / 65535), 65535) as int def hue = Math.min(Math.round(bulb.value.state.hue * 100 / 65535), 65535) as int
def sat = Math.round(bulb.value.state.sat * 100 / 255) as int def sat = Math.round(bulb.value.state.sat * 100 / 255) as int
def hex = colorUtil.hslToHex(hue, sat) def hex = colorUtil.hslToHex(hue, sat)
sendEvent(d.deviceNetworkId, [name: "color", value: hex]) sendEvent(d.deviceNetworkId, [name: "color", value: hex])
sendEvent(d.deviceNetworkId, [name: "hue", value: hue]) sendEvent(d.deviceNetworkId, [name: "hue", value: hue])
sendEvent(d.deviceNetworkId, [name: "saturation", value: sat]) sendEvent(d.deviceNetworkId, [name: "saturation", value: sat])
} }
} else { } else {
sendEvent(d.deviceNetworkId, [name: "switch", value: "off"]) sendEvent(d.deviceNetworkId, [name: "switch", value: "off"])
sendEvent(d.deviceNetworkId, [name: "level", value: 100]) sendEvent(d.deviceNetworkId, [name: "level", value: 100])
if (bulb.value.state.sat) { if (bulb.value.state.sat) {
def hue = 23 def hue = 23
def sat = 56 def sat = 56
def hex = colorUtil.hslToHex(23, 56) def hex = colorUtil.hslToHex(23, 56)
sendEvent(d.deviceNetworkId, [name: "color", value: hex]) sendEvent(d.deviceNetworkId, [name: "color", value: hex])
sendEvent(d.deviceNetworkId, [name: "hue", value: hue]) sendEvent(d.deviceNetworkId, [name: "hue", value: hue])
sendEvent(d.deviceNetworkId, [name: "saturation", value: sat]) sendEvent(d.deviceNetworkId, [name: "saturation", value: sat])
} }
} }
} }
}
}
} }
else else
{ //put response { //put response
@@ -559,25 +576,25 @@ def parse(childDevice, description) {
} }
} }
} }
} else { } else {
log.debug "parse - got something other than headers,body..." log.debug "parse - got something other than headers,body..."
return [] return []
} }
} }
def on(childDevice, transition = 4) { def on(childDevice, transition_deprecated = 0) {
log.debug "Executing 'on'" log.debug "Executing 'on'"
// Assume bulb is off if no current state is found for level to avoid bulbs getting stuck in off after initial discovery def percent = childDevice.device?.currentValue("level") as Integer
def percent = childDevice.device?.currentValue("level") as Integer ?: 0
def level = Math.min(Math.round(percent * 255 / 100), 255) def level = Math.min(Math.round(percent * 255 / 100), 255)
put("lights/${getId(childDevice)}/state", [bri: level, on: true, transitiontime: transition]) put("lights/${getId(childDevice)}/state", [bri: level, on: true])
return "level: $percent" return "level: $percent"
} }
def off(childDevice, transition = 4) { def off(childDevice, transition_deprecated = 0) {
log.debug "Executing 'off'" log.debug "Executing 'off'"
put("lights/${getId(childDevice)}/state", [on: false, transitiontime: transition]) put("lights/${getId(childDevice)}/state", [on: false])
return "level: 0"
} }
def setLevel(childDevice, percent) { def setLevel(childDevice, percent) {
@@ -598,19 +615,21 @@ def setHue(childDevice, percent) {
put("lights/${getId(childDevice)}/state", [hue: level]) put("lights/${getId(childDevice)}/state", [hue: level])
} }
def setColor(childDevice, color, alert = "none", transition = 4) { def setColor(childDevice, huesettings, alert_deprecated = "", transition_deprecated = 0) {
log.debug "Executing 'setColor($color)'" log.debug "Executing 'setColor($huesettings)'"
def hue = Math.min(Math.round(color.hue * 65535 / 100), 65535) def hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
def sat = Math.min(Math.round(color.saturation * 255 / 100), 255) def sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
def alert = huesettings.alert ? huesettings.alert : "none"
def transition = huesettings.transition ? huesettings.transition : 4
def value = [sat: sat, hue: hue, alert: alert, transitiontime: transition] def value = [sat: sat, hue: hue, alert: alert, transitiontime: transition]
if (color.level != null) { if (huesettings.level != null) {
value.bri = Math.min(Math.round(color.level * 255 / 100), 255) value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
value.on = value.bri > 0 value.on = value.bri > 0
} }
if (color.switch) { if (huesettings.switch) {
value.on = color.switch == "on" value.on = huesettings.switch == "on"
} }
log.debug "sending command $value" log.debug "sending command $value"
@@ -640,15 +659,19 @@ private getId(childDevice) {
private poll() { private poll() {
def host = getBridgeIP() def host = getBridgeIP()
def uri = "/api/${state.username}/lights/" def uri = "/api/${state.username}/lights/"
log.debug "GET: $host$uri" try {
sendHubCommand(new physicalgraph.device.HubAction("""GET ${uri} HTTP/1.1 sendHubCommand(new physicalgraph.device.HubAction("""GET ${uri} HTTP/1.1
HOST: ${host} HOST: ${host}
""", physicalgraph.device.Protocol.LAN, selectedHue)) """, physicalgraph.device.Protocol.LAN, selectedHue))
} catch (all) {
log.warn "Parsing Body failed - trying again..."
doDeviceSync()
}
} }
private put(path, body) { private put(path, body) {
def host = getBridgeIP() def host = getBridgeIP()
def uri = "/api/${state.username}/$path" def uri = "/api/${state.username}/$path"
def bodyJSON = new groovy.json.JsonBuilder(body).toString() def bodyJSON = new groovy.json.JsonBuilder(body).toString()
def length = bodyJSON.getBytes().size().toString() def length = bodyJSON.getBytes().size().toString()
@@ -668,9 +691,13 @@ ${bodyJSON}
private getBridgeIP() { private getBridgeIP() {
def host = null def host = null
if (selectedHue) { if (selectedHue) {
def d = getChildDevice(dni) def d = getChildDevice(selectedHue)
if (d) if (d) {
host = d.latestState('networkAddress').stringValue if (d.getDeviceDataByName("networkAddress"))
host = d.getDeviceDataByName("networkAddress")
else
host = d.latestState('networkAddress').stringValue
}
if (host == null || host == "") { if (host == null || host == "") {
def serialNumber = selectedHue def serialNumber = selectedHue
def bridge = getHueBridges().find { it?.value?.serialNumber?.equalsIgnoreCase(serialNumber) }?.value def bridge = getHueBridges().find { it?.value?.serialNumber?.equalsIgnoreCase(serialNumber) }?.value
@@ -681,9 +708,9 @@ private getBridgeIP() {
host = "${convertHexToIP(bridge?.ip)}:${convertHexToInt(bridge?.port)}" host = "${convertHexToIP(bridge?.ip)}:${convertHexToInt(bridge?.port)}"
} else if (bridge?.networkAddress && bridge?.deviceAddress) } else if (bridge?.networkAddress && bridge?.deviceAddress)
host = "${convertHexToIP(bridge?.networkAddress)}:${convertHexToInt(bridge?.deviceAddress)}" host = "${convertHexToIP(bridge?.networkAddress)}:${convertHexToInt(bridge?.deviceAddress)}"
} }
log.trace "Bridge: $selectedHue - Host: $host" log.trace "Bridge: $selectedHue - Host: $host"
} }
return host return host
} }