From 94f57dd249268bcb6e3ba014a482ac28f2db5f11 Mon Sep 17 00:00:00 2001 From: Lars Finander Date: Fri, 14 Oct 2016 16:48:14 -0600 Subject: [PATCH] DVCSMP-2088 Philips Hue: Add explicit online events --- .../hue-bloom.src/hue-bloom.groovy | 5 +- .../hue-bridge.src/hue-bridge.groovy | 5 +- .../smartthings/hue-bulb.src/hue-bulb.groovy | 5 +- .../hue-lux-bulb.src/hue-lux-bulb.groovy | 5 +- .../hue-white-ambiance-bulb.groovy | 5 +- .../hue-connect.src/hue-connect.groovy | 408 +++++++++--------- 6 files changed, 206 insertions(+), 227 deletions(-) diff --git a/devicetypes/smartthings/hue-bloom.src/hue-bloom.groovy b/devicetypes/smartthings/hue-bloom.src/hue-bloom.groovy index e6a02ab..ae48f0d 100644 --- a/devicetypes/smartthings/hue-bloom.src/hue-bloom.groovy +++ b/devicetypes/smartthings/hue-bloom.src/hue-bloom.groovy @@ -57,7 +57,7 @@ metadata { } void installed() { - sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false) + sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") } // parse events into attributes @@ -172,6 +172,3 @@ def verifyPercent(percent) { } } -def ping() { - log.debug "${parent.ping(this)}" -} diff --git a/devicetypes/smartthings/hue-bridge.src/hue-bridge.groovy b/devicetypes/smartthings/hue-bridge.src/hue-bridge.groovy index fee818f..e7fbce1 100644 --- a/devicetypes/smartthings/hue-bridge.src/hue-bridge.groovy +++ b/devicetypes/smartthings/hue-bridge.src/hue-bridge.groovy @@ -45,7 +45,7 @@ metadata { } void installed() { - sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false) + sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") } // parse events into attributes @@ -87,6 +87,3 @@ def parse(description) { results } -def ping() { - log.debug "${parent.ping(this)}" -} diff --git a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy index 4dbae74..143b0b3 100644 --- a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy +++ b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy @@ -66,7 +66,7 @@ metadata { } void installed() { - sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false) + sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") } // parse events into attributes @@ -188,6 +188,3 @@ def verifyPercent(percent) { } } -def ping() { - log.trace "${parent.ping(this)}" -} diff --git a/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy b/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy index 158e11a..d855d38 100644 --- a/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy +++ b/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy @@ -50,7 +50,7 @@ metadata { } void installed() { - sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false) + sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") } // parse events into attributes @@ -93,6 +93,3 @@ void refresh() { parent.manualRefresh() } -def ping() { - log.debug "${parent.ping(this)}" -} diff --git a/devicetypes/smartthings/hue-white-ambiance-bulb.src/hue-white-ambiance-bulb.groovy b/devicetypes/smartthings/hue-white-ambiance-bulb.src/hue-white-ambiance-bulb.groovy index 10315a5..718ed57 100644 --- a/devicetypes/smartthings/hue-white-ambiance-bulb.src/hue-white-ambiance-bulb.groovy +++ b/devicetypes/smartthings/hue-white-ambiance-bulb.src/hue-white-ambiance-bulb.groovy @@ -55,7 +55,7 @@ metadata { } void installed() { - sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false) + sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}") } // parse events into attributes @@ -107,6 +107,3 @@ void refresh() { parent.manualRefresh() } -def ping() { - log.debug "${parent.ping(this)}" -} diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index d39d451..7ee6e60 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -8,7 +8,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at: * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License @@ -24,7 +24,7 @@ definition( category: "SmartThings Labs", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/hue.png", iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/hue@2x.png", - singleInstance: true + singleInstance: true ) preferences { @@ -110,7 +110,7 @@ def bridgeLinking() { if (state.refreshUsernameNeeded) { paragraphText = "The current Hue username is invalid.\n\nPlease press the button on your Hue Bridge to re-link. " } else { - paragraphText = "Press the button on your Hue Bridge to setup a link. " + paragraphText = "Press the button on your Hue Bridge to setup a link. " } } else { paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next." @@ -198,7 +198,7 @@ void ssdpSubscribe() { private sendDeveloperReq() { def token = app.id - def host = getBridgeIP() + def host = getBridgeIP() sendHubCommand(new physicalgraph.device.HubAction([ method: "POST", path: "/api", @@ -209,7 +209,7 @@ private sendDeveloperReq() { } private discoverHueBulbs() { - def host = getBridgeIP() + def host = getBridgeIP() sendHubCommand(new physicalgraph.device.HubAction([ method: "GET", path: "/api/${state.username}/lights", @@ -231,8 +231,8 @@ private verifyHueBridge(String deviceNetworkId, String host) { private verifyHueBridges() { def devices = getHueBridges().findAll { it?.value?.verified != true } devices.each { - def ip = convertHexToIP(it.value.networkAddress) - def port = convertHexToInt(it.value.deviceAddress) + def ip = convertHexToIP(it.value.networkAddress) + def port = convertHexToInt(it.value.deviceAddress) verifyHueBridge("${it.value.mac}", (ip + ":" + port)) } } @@ -261,7 +261,7 @@ Map bulbsDiscovered() { bulbs.each { def value = "${it.name}" def key = app.id +"/"+ it.id - logg += "$value - $key, " + logg += "$value - $key, " bulbmap["${key}"] = value } } @@ -288,22 +288,22 @@ def installed() { def updated() { log.trace "Updated with settings: ${settings}" unsubscribe() - unschedule() + unschedule() initialize() } def initialize() { log.debug "Initializing" - unsubscribe(bridge) - state.inBulbDiscovery = false - state.bridgeRefreshCount = 0 - state.bulbRefreshCount = 0 + unsubscribe(bridge) + state.inBulbDiscovery = false + state.bridgeRefreshCount = 0 + state.bulbRefreshCount = 0 state.updating = false if (selectedHue) { - addBridge() - addBulbs() - doDeviceSync() - runEvery5Minutes("doDeviceSync") + addBridge() + addBulbs() + doDeviceSync() + runEvery5Minutes("doDeviceSync") } } @@ -369,7 +369,6 @@ def addBulbs() { 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" @@ -399,23 +398,23 @@ def addBridge() { if(vbridge) { def d = getChildDevice(selectedHue) if(!d) { - // compatibility with old devices - def newbridge = true - childDevices.each { - if (it.getDeviceDataByName("mac")) { - def newDNI = "${it.getDeviceDataByName("mac")}" - if (newDNI != it.deviceNetworkId) { - def oldDNI = it.deviceNetworkId - log.debug "updating dni for device ${it} with $newDNI - previous DNI = ${it.deviceNetworkId}" - it.setDeviceNetworkId("${newDNI}") + // compatibility with old devices + def newbridge = true + childDevices.each { + if (it.getDeviceDataByName("mac")) { + def newDNI = "${it.getDeviceDataByName("mac")}" + if (newDNI != it.deviceNetworkId) { + def oldDNI = it.deviceNetworkId + log.debug "updating dni for device ${it} with $newDNI - previous DNI = ${it.deviceNetworkId}" + it.setDeviceNetworkId("${newDNI}") if (oldDNI == selectedHue) { app.updateSetting("selectedHue", newDNI) } - newbridge = false - } - } - } - if (newbridge) { + newbridge = false + } + } + } + if (newbridge) { // Hue uses last 6 digits of MAC address as ID number, this number is shown on the bottom of the bridge def idNumber = getBridgeIdNumber(selectedHue) d = addChildDevice("smartthings", "Hue Bridge", selectedHue, vbridge.value.hub, ["label": "Hue Bridge ($idNumber)"]) @@ -426,9 +425,11 @@ def addBridge() { d.completedSetup = true log.debug "created ${d.displayName} with id ${d.deviceNetworkId}" def childDevice = getChildDevice(d.deviceNetworkId) + childDevice?.sendEvent(name: "status", value: "Online") + childDevice?.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true) updateBridgeStatus(childDevice) - childDevice.sendEvent(name: "idNumber", value: idNumber) + childDevice?.sendEvent(name: "idNumber", value: idNumber) if (vbridge.value.ip && vbridge.value.port) { if (vbridge.value.ip.contains(".")) { childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port) @@ -580,47 +581,47 @@ void usernameHandler(physicalgraph.device.HubResponse hubResponse) { @Deprecated def locationHandler(evt) { def description = evt.description - log.trace "Location: $description" + log.trace "Location: $description" def hub = evt?.hubId def parsedEvent = parseLanMessage(description) parsedEvent << ["hub":hub] if (parsedEvent?.ssdpTerm?.contains("urn:schemas-upnp-org:device:basic:1")) { - //SSDP DISCOVERY EVENTS + //SSDP DISCOVERY EVENTS log.trace "SSDP DISCOVERY EVENTS" def bridges = getHueBridges() log.trace bridges.toString() if (!(bridges."${parsedEvent.ssdpUSN.toString()}")) { - //bridge does not exist + //bridge does not exist log.trace "Adding bridge ${parsedEvent.ssdpUSN}" bridges << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent] } else { // update the values - def ip = convertHexToIP(parsedEvent.networkAddress) - def port = convertHexToInt(parsedEvent.deviceAddress) - def host = ip + ":" + port + def ip = convertHexToIP(parsedEvent.networkAddress) + def port = convertHexToInt(parsedEvent.deviceAddress) + def host = ip + ":" + port log.debug "Device ($parsedEvent.mac) was already found in state with ip = $host." - def dstate = bridges."${parsedEvent.ssdpUSN.toString()}" + def dstate = bridges."${parsedEvent.ssdpUSN.toString()}" def dni = "${parsedEvent.mac}" - def d = getChildDevice(dni) - def networkAddress = null - if (!d) { - childDevices.each { - if (it.getDeviceDataByName("mac")) { - def newDNI = "${it.getDeviceDataByName("mac")}" + def d = getChildDevice(dni) + def networkAddress = null + if (!d) { + childDevices.each { + if (it.getDeviceDataByName("mac")) { + def newDNI = "${it.getDeviceDataByName("mac")}" d = it - if (newDNI != it.deviceNetworkId) { - def oldDNI = it.deviceNetworkId - log.debug "updating dni for device ${it} with $newDNI - previous DNI = ${it.deviceNetworkId}" - it.setDeviceNetworkId("${newDNI}") + if (newDNI != it.deviceNetworkId) { + def oldDNI = it.deviceNetworkId + log.debug "updating dni for device ${it} with $newDNI - previous DNI = ${it.deviceNetworkId}" + it.setDeviceNetworkId("${newDNI}") if (oldDNI == selectedHue) { app.updateSetting("selectedHue", newDNI) } - doDeviceSync() - } - } - } + doDeviceSync() + } + } + } } else { updateBridgeStatus(d) if (d.getDeviceDataByName("networkAddress")) { @@ -628,22 +629,22 @@ def locationHandler(evt) { } else { networkAddress = d.latestState('networkAddress').stringValue } - log.trace "Host: $host - $networkAddress" - if(host != networkAddress) { - log.debug "Device's port or ip changed for device $d..." - dstate.ip = ip - dstate.port = port - dstate.name = "Philips hue ($ip)" - d.sendEvent(name:"networkAddress", value: host) - d.updateDataValue("networkAddress", host) - } - } + log.trace "Host: $host - $networkAddress" + if(host != networkAddress) { + log.debug "Device's port or ip changed for device $d..." + dstate.ip = ip + dstate.port = port + dstate.name = "Philips hue ($ip)" + d.sendEvent(name:"networkAddress", value: host) + d.updateDataValue("networkAddress", host) + } + } } } else if (parsedEvent.headers && parsedEvent.body) { log.trace "HUE BRIDGE RESPONSES" def headerString = parsedEvent.headers.toString() if (headerString?.contains("xml")) { - log.trace "description.xml response (application/xml)" + log.trace "description.xml response (application/xml)" def body = new XmlSlurper().parseText(parsedEvent.body) if (body?.device?.modelName?.text().startsWith("Philips hue bridge")) { def bridges = getHueBridges() @@ -655,7 +656,7 @@ def locationHandler(evt) { } } } else if(headerString?.contains("json") && isValidSource(parsedEvent.mac)) { - log.trace "description.xml response (application/json)" + log.trace "description.xml response (application/json)" def body = new groovy.json.JsonSlurper().parseText(parsedEvent.body) if (body.success != null) { if (body.success[0] != null) { @@ -692,7 +693,7 @@ def doDeviceSync(){ poll() ssdpSubscribe() discoverBridges() - checkBridgeStatus() + checkBridgeStatus() } /** @@ -705,10 +706,14 @@ def doDeviceSync(){ private void updateBridgeStatus(childDevice) { // Update activity timestamp if child device is a valid bridge def vbridges = getVerifiedHueBridges() - def vbridge = vbridges.find {"${it.value.mac}".toUpperCase() == childDevice?.device?.deviceNetworkId?.toUpperCase()} + def vbridge = vbridges.find { + "${it.value.mac}".toUpperCase() == childDevice?.device?.deviceNetworkId?.toUpperCase() + } vbridge?.value?.lastActivity = now() - if(vbridge) { + if (vbridge && childDevice?.device?.currentValue("status") == "Offline") { + log.debug "$childDevice is back Online" childDevice?.sendEvent(name: "status", value: "Online") + childDevice?.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true) } } @@ -717,29 +722,37 @@ private void updateBridgeStatus(childDevice) { * for the bridge and all connected lights. Also, set ID number on bridge if not done previously. */ private void checkBridgeStatus() { - def bridges = getHueBridges() - // Check if each bridge has been heard from within the last 11 minutes (2 poll intervals times 5 minutes plus buffer) - def time = now() - (1000 * 60 * 11) - bridges.each { - def d = getChildDevice(it.value.mac) - if(d) { - // Set id number on bridge if not done - if (it.value.idNumber == null) { - it.value.idNumber = getBridgeIdNumber(it.value.serialNumber) + def bridges = getHueBridges() + // Check if each bridge has been heard from within the last 11 minutes (2 poll intervals times 5 minutes plus buffer) + def time = now() - (1000 * 60 * 11) + bridges.each { + def d = getChildDevice(it.value.mac) + if (d) { + // Set id number on bridge if not done + if (it.value.idNumber == null) { + it.value.idNumber = getBridgeIdNumber(it.value.serialNumber) d.sendEvent(name: "idNumber", value: it.value.idNumber) - } + } if (it.value.lastActivity < time) { // it.value.lastActivity != null && - log.warn "Bridge $it.value.idNumber is Offline" - d.sendEvent(name: "status", value: "Offline") + if (d.currentStatus == "Online") { + log.warn "$d is Offline" + d.sendEvent(name: "status", value: "Offline") + d.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false, isStateChange: true) - state.bulbs?.each { - it.value.online = false + Calendar currentTime = Calendar.getInstance() + getChildDevices().each { + def id = getId(it) + if (state.bulbs[id]?.online == true) { + state.bulbs[id]?.online = false + state.bulbs[id]?.unreachableSince = currentTime.getTimeInMillis() + it.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false, isStateChange: true) + } + } } - getChildDevices().each { - it.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", isStateChange: true, displayed: false) - } - } else { + } else if (d.currentStatus == "Offline") { + log.debug "$d is back Online" + d.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true) d.sendEvent(name: "status", value: "Online")//setOnline(false) } } @@ -791,24 +804,24 @@ def parse(childDevice, description) { def parsedEvent = parseLanMessage(description) if (parsedEvent.headers && parsedEvent.body) { def headerString = parsedEvent.headers.toString() - def bodyString = parsedEvent.body.toString() + def bodyString = parsedEvent.body.toString() if (headerString?.contains("json")) { - def body - try { - body = new groovy.json.JsonSlurper().parseText(bodyString) - } catch (all) { - log.warn "Parsing Body failed - trying again..." - poll() - } - if (body instanceof java.util.Map) { - // get (poll) reponse - return handlePoll(body) + def body + try { + body = new groovy.json.JsonSlurper().parseText(bodyString) + } catch (all) { + log.warn "Parsing Body failed - trying again..." + poll() + } + if (body instanceof java.util.Map) { + // get (poll) reponse + return handlePoll(body) } else { //put response return handleCommandResponse(body) - } - } - } else { + } + } + } else { log.debug "parse - got something other than headers,body..." return [] } @@ -829,19 +842,19 @@ private sendColorEvents(device, xy, hue, sat, ct, colormode = null) { device.sendEvent([name: "colorTemperature", value: temp, descriptionText: "Color temperature has changed"]) // Return because color temperature change is not counted as a color change in SmartThings so no hex update necessary return - } + } if (hue != null) { // 0-65535 def value = Math.min(Math.round(hue * 100 / 65535), 65535) as int events["hue"] = [name: "hue", value: value, descriptionText: "Color has changed", displayed: false] - } + } if (sat != null) { // 0-254 def value = Math.round(sat * 100 / 254) as int - events["saturation"] = [name: "saturation", value: value, descriptionText: "Color has changed", displayed: false] - } + events["saturation"] = [name: "saturation", value: value, descriptionText: "Color has changed", displayed: false] + } // Following is used to decide what to base hex calculations on since it is preferred to return a colorchange in hex if (xy != null && colormode != "hs") { @@ -943,12 +956,9 @@ private handleCommandResponse(body) { updates.each { childDeviceNetworkId, params -> def device = getChildDevice(childDeviceNetworkId) def id = getId(device) - // If device is offline, then don't send events which will update device watch - if (isOnline(id)) { - sendBasicEvents(device, "on", params.on) - sendBasicEvents(device, "bri", params.bri) - sendColorEvents(device, params.xy, params.hue, params.sat, params.ct) - } + sendBasicEvents(device, "on", params.on) + sendBasicEvents(device, "bri", params.bri) + sendColorEvents(device, params.xy, params.hue, params.sat, params.ct) } return [] } @@ -981,39 +991,38 @@ private handlePoll(body) { def bulbs = getChildDevices() for (bulb in body) { - def device = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"} + def device = bulbs.find { it.deviceNetworkId == "${app.id}/${bulb.key}" } if (device) { if (bulb.value.state?.reachable) { - if (state.bulbs[bulb.key]?.online == false) { + if (state.bulbs[bulb.key]?.online == false || state.bulbs[bulb.key]?.online == null) { // light just came back online, notify device watch - def lastActivity = now() - device.sendEvent(name: "deviceWatch-status", value: "ONLINE", description: "Last Activity is on ${new Date((long) lastActivity)}", displayed: false, isStateChange: true) + device.sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false, isStateChange: true) log.debug "$device is Online" } // Mark light as "online" state.bulbs[bulb.key]?.unreachableSince = null state.bulbs[bulb.key]?.online = true - - // If user just executed commands, then do not send events to avoid confusing the turning on/off state - if (!state.updating) { - sendBasicEvents(device, "on", bulb.value?.state?.on) - sendBasicEvents(device, "bri", bulb.value?.state?.bri) - sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode) - } } else { if (state.bulbs[bulb.key]?.unreachableSince == null) { // Store the first time where device was reported as "unreachable" state.bulbs[bulb.key]?.unreachableSince = currentTime.getTimeInMillis() - } else if (state.bulbs[bulb.key]?.online) { + } + if (state.bulbs[bulb.key]?.online || state.bulbs[bulb.key]?.online == null) { // Check if device was "unreachable" for more than 11 minutes and mark "offline" if necessary - if (state.bulbs[bulb.key]?.unreachableSince < time11.getTimeInMillis()) { + if (state.bulbs[bulb.key]?.unreachableSince < time11.getTimeInMillis() || state.bulbs[bulb.key]?.online == null) { log.warn "$device went Offline" state.bulbs[bulb.key]?.online = false - device.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", displayed: false, isStateChange: true) + device.sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false, isStateChange: true) } } log.warn "$device may not reachable by Hue bridge" } + // If user just executed commands, then do not send events to avoid confusing the turning on/off state + if (!state.updating) { + sendBasicEvents(device, "on", bulb.value?.state?.on) + sendBasicEvents(device, "bri", bulb.value?.state?.bri) + sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode) + } } } return [] @@ -1032,16 +1041,16 @@ def updateHandler() { def hubVerification(bodytext) { log.trace "Bridge sent back description.xml for verification" - def body = new XmlSlurper().parseText(bodytext) - if (body?.device?.modelName?.text().startsWith("Philips hue bridge")) { - 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] - } else { - log.error "/description.xml returned a bridge that didn't exist" - } - } + def body = new XmlSlurper().parseText(bodytext) + if (body?.device?.modelName?.text().startsWith("Philips hue bridge")) { + 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] + } else { + log.error "/description.xml returned a bridge that didn't exist" + } + } } def on(childDevice) { @@ -1050,7 +1059,7 @@ def on(childDevice) { updateInProgress() createSwitchEvent(childDevice, "on") put("lights/$id/state", [on: true]) - return "Bulb is turning On" + return "Bulb is turning On" } def off(childDevice) { @@ -1059,15 +1068,15 @@ def off(childDevice) { updateInProgress() createSwitchEvent(childDevice, "off") put("lights/$id/state", [on: false]) - return "Bulb is turning Off" + return "Bulb is turning Off" } def setLevel(childDevice, percent) { log.debug "Executing 'setLevel'" def id = getId(childDevice) - updateInProgress() + updateInProgress() // 1 - 254 - def level + def level if (percent == 1) level = 1 else @@ -1101,7 +1110,7 @@ def setSaturation(childDevice, percent) { def setHue(childDevice, percent) { log.debug "Executing 'setHue($percent)'" def id = getId(childDevice) - updateInProgress() + updateInProgress() // 0 - 65535 def level = Math.min(Math.round(percent * 65535 / 100), 65535) // TODO should this be done by app only or should we default to on? @@ -1113,7 +1122,7 @@ def setHue(childDevice, percent) { def setColorTemperature(childDevice, huesettings) { log.debug "Executing 'setColorTemperature($huesettings)'" def id = getId(childDevice) - updateInProgress() + updateInProgress() // 153 (6500K) to 500 (2000K) def ct = hueSettings == 6500 ? 153 : Math.round(1000000/huesettings) createSwitchEvent(childDevice, "on") @@ -1122,14 +1131,14 @@ def setColorTemperature(childDevice, huesettings) { } def setColor(childDevice, huesettings) { - log.debug "Executing 'setColor($huesettings)'" + log.debug "Executing 'setColor($huesettings)'" def id = getId(childDevice) - updateInProgress() + updateInProgress() - def value = [:] - def hue = null - def sat = null - def xy = null + def value = [:] + def hue = null + def sat = null + def xy = null // Prefer hue/sat over hex to make sure it works with the majority of the smartapps if (huesettings.hue != null || huesettings.sat != null) { @@ -1154,48 +1163,32 @@ def setColor(childDevice, huesettings) { // value.xy = calculateXY(hex, model) // Once groups, or scenes are introduced it might be a good idea to use unique models again value.xy = calculateXY(hex) - } + } */ - // Default behavior is to turn light on - value.on = true + // Default behavior is to turn light on + value.on = true - if (huesettings.level != null) { - if (huesettings.level <= 0) - value.on = false - else if (huesettings.level == 1) - value.bri = 1 - else + if (huesettings.level != null) { + if (huesettings.level <= 0) + value.on = false + else if (huesettings.level == 1) + value.bri = 1 + else value.bri = Math.min(Math.round(huesettings.level * 254 / 100), 254) - } - value.alert = huesettings.alert ? huesettings.alert : "none" - value.transitiontime = huesettings.transitiontime ? huesettings.transitiontime : 4 + } + value.alert = huesettings.alert ? huesettings.alert : "none" + value.transitiontime = huesettings.transitiontime ? huesettings.transitiontime : 4 - // Make sure to turn off light if requested - if (huesettings.switch == "off") - value.on = false + // Make sure to turn off light if requested + if (huesettings.switch == "off") + value.on = false createSwitchEvent(childDevice, value.on ? "on" : "off") - put("lights/$id/state", value) + put("lights/$id/state", value) return "Setting color to $value" } -def ping(childDevice) { - if (childDevice.device?.deviceNetworkId?.equalsIgnoreCase(selectedHue)) { - if (childDevice.device?.currentValue("status")?.equalsIgnoreCase("Online")) { - childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Bridge is reachable", displayed: false, isStateChange: true) - return "Bridge is Online" - } else { - return "Bridge is Offline" - } - } else if (isOnline(getId(childDevice))) { - childDevice.sendEvent(name: "deviceWatch-ping", value: "ONLINE", description: "Hue Light is reachable", displayed: false, isStateChange: true) - return "Device is Online" - } else { - return "Device is Offline" - } -} - private getId(childDevice) { if (childDevice.device?.deviceNetworkId?.startsWith("HUE")) { return childDevice.device?.deviceNetworkId[3..-1] @@ -1246,30 +1239,30 @@ private getBridgeIdNumber(serialNumber) { private getBridgeIP() { def host = null if (selectedHue) { - def d = getChildDevice(selectedHue) - if (d) { - if (d.getDeviceDataByName("networkAddress")) - host = d.getDeviceDataByName("networkAddress") - else - host = d.latestState('networkAddress').stringValue - } - if (host == null || host == "") { - def serialNumber = selectedHue - def bridge = getHueBridges().find { it?.value?.serialNumber?.equalsIgnoreCase(serialNumber) }?.value - if (!bridge) { - bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(serialNumber) }?.value - } - if (bridge?.ip && bridge?.port) { - if (bridge?.ip.contains(".")) - host = "${bridge?.ip}:${bridge?.port}" - else - host = "${convertHexToIP(bridge?.ip)}:${convertHexToInt(bridge?.port)}" - } else if (bridge?.networkAddress && bridge?.deviceAddress) - host = "${convertHexToIP(bridge?.networkAddress)}:${convertHexToInt(bridge?.deviceAddress)}" - } - log.trace "Bridge: $selectedHue - Host: $host" - } - return host + def d = getChildDevice(selectedHue) + if (d) { + if (d.getDeviceDataByName("networkAddress")) + host = d.getDeviceDataByName("networkAddress") + else + host = d.latestState('networkAddress').stringValue + } + if (host == null || host == "") { + def serialNumber = selectedHue + def bridge = getHueBridges().find { it?.value?.serialNumber?.equalsIgnoreCase(serialNumber) }?.value + if (!bridge) { + bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(serialNumber) }?.value + } + if (bridge?.ip && bridge?.port) { + if (bridge?.ip.contains(".")) + host = "${bridge?.ip}:${bridge?.port}" + else + host = "${convertHexToIP(bridge?.ip)}:${convertHexToInt(bridge?.port)}" + } else if (bridge?.networkAddress && bridge?.deviceAddress) + host = "${convertHexToIP(bridge?.networkAddress)}:${convertHexToInt(bridge?.deviceAddress)}" + } + log.trace "Bridge: $selectedHue - Host: $host" + } + return host } private Integer convertHexToInt(hex) { @@ -1309,7 +1302,7 @@ private List getRealHubFirmwareVersions() { * @param childDevice device to send event for * @param setSwitch The new switch state, "on" or "off" * @param setLevel Optional, switchLevel between 0-100, used if you set level to 0 for example since - * that should generate "off" instead of level change + * that should generate "off" instead of level change */ private void createSwitchEvent(childDevice, setSwitch, setLevel = null) { @@ -1452,8 +1445,8 @@ private float[] calculateXY(colorStr, model = null) { xy[0] = closestPoint.x; xy[1] = closestPoint.y; } - // xy[0] = PHHueHelper.precision(4, xy[0]); - // xy[1] = PHHueHelper.precision(4, xy[1]); + // xy[0] = PHHueHelper.precision(4, xy[0]); + // xy[1] = PHHueHelper.precision(4, xy[1]); // TODO needed, assume it just sets number of decimals? @@ -1471,7 +1464,7 @@ private float[] calculateXY(colorStr, model = null) { * * @param points the float array contain x and the y value. [x,y] * @param model the model of the lamp, example: "LCT001" for hue bulb. Used to calculate the color gamut. - * If this value is empty the default gamut values are used. + * If this value is empty the default gamut values are used. * @return the color value in hex (#ff03d3). If xy is null OR xy is not an array of size 2, Color. BLACK will be returned */ private String colorFromXY(points, model ) { @@ -1785,3 +1778,4 @@ def hsvToHex(hue, sat, value = 100){ return "#$r1$g1$b1" } +