From 4f188581dfeb467b682bc91a48d71671853f7f6c Mon Sep 17 00:00:00 2001 From: Lars Finander Date: Fri, 16 Sep 2016 19:59:09 -0600 Subject: [PATCH 1/5] INC-6888 Philips Hue: Correct incorrect bridge mac --- .../hue-connect.src/hue-connect.groovy | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index fd3beb0..8afc7cb 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -333,9 +333,9 @@ def bulbListHandler(hub, data = "") { def bridge = null if (selectedHue) { bridge = getChildDevice(selectedHue) + bridge?.sendEvent(name: "bulbList", value: hub, data: bulbs, isStateChange: true, displayed: false) } - bridge.sendEvent(name: "bulbList", value: hub, data: bulbs, isStateChange: true, displayed: false) - msg = "${bulbs.size()} bulbs found. ${bulbs}" + msg = "${bulbs.size()} bulbs found. ${bulbs}" return msg } @@ -490,24 +490,25 @@ def ssdpBridgeHandler(evt) { def host = ip + ":" + port log.debug "Device ($parsedEvent.mac) was already found in state with ip = $host." def dstate = bridges."${parsedEvent.ssdpUSN.toString()}" - def dni = "${parsedEvent.mac}" - def d = getChildDevice(dni) + def dniReceived = "${parsedEvent.mac}" + def currentDni = dstate.mac + def d = getChildDevice(dniReceived) 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 (oldDNI == selectedHue) { - app.updateSetting("selectedHue", newDNI) - } - doDeviceSync() - } + // There might be a mismatch between bridge DNI and the actual bridge mac address, correct that + log.debug "Bridge with $dniReceived not found" + def bridge = childDevices.find { it.deviceNetworkId == currentDni } + if (bridge != null) { + log.warn "Bridge is set to ${bridge.deviceNetworkId}, updating to $dniReceived" + bridge.setDeviceNetworkId("${dniReceived}") + dstate.mac = dniReceived + // Check to see if selectedHue is a valid bridge, otherwise update it + def isSelectedValid = bridges?.find {it.value?.mac == selectedHue} + if (isSelectedValid == null) { + log.warn "Correcting selectedHue in state" + app.updateSetting("selectedHue", dniReceived) } + doDeviceSync() } } else { updateBridgeStatus(d) @@ -525,6 +526,18 @@ def ssdpBridgeHandler(evt) { d.sendEvent(name:"networkAddress", value: host) d.updateDataValue("networkAddress", host) } + if (dstate.mac != dniReceived) { + log.warn "Correcting bridge mac address in state" + dstate.mac = dniReceived + } + if (selectedHue != dniReceived) { + // Check to see if selectedHue is a valid bridge, otherwise update it + def isSelectedValid = bridges?.find {it.value?.mac == selectedHue} + if (isSelectedValid == null) { + log.warn "Correcting selectedHue in state" + app.updateSetting("selectedHue", dniReceived) + } + } } } } From 2f8ed277ffe56265a52df845a5c176ae3f0f3064 Mon Sep 17 00:00:00 2001 From: Lars Finander Date: Thu, 22 Sep 2016 12:07:09 -0600 Subject: [PATCH 2/5] SSVD-2798 Philips Hue: Bridge keeps getting unchecked during discovery --- smartapps/smartthings/hue-connect.src/hue-connect.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index fd3beb0..033f53c 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -83,7 +83,7 @@ def bridgeDiscovery(params=[:]) return dynamicPage(name:"bridgeDiscovery", title:"Discovery Started!", nextPage:"bridgeBtnPush", refreshInterval:refreshInterval, uninstall: true) { section("Please wait while we discover your Hue Bridge. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") { - input "selectedHue", "enum", required:false, title:"Select Hue Bridge (${numFound} found)", multiple:false, options:options + input "selectedHue", "enum", required:false, title:"Select Hue Bridge (${numFound} found)", multiple:false, options:options, submitOnChange: true } } } From f5c3997679f03396df3b8484416e426944867d0e Mon Sep 17 00:00:00 2001 From: Lars Finander Date: Fri, 23 Sep 2016 16:31:07 -0600 Subject: [PATCH 3/5] DVCSMP-2081 Philips Hue: Bridge is throwing 650k exceptions a day --- .../hue-connect.src/hue-connect.groovy | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index 033f53c..6269b93 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -1120,7 +1120,7 @@ def setColor(childDevice, huesettings) { value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535) if (huesettings.saturation != null) value.sat = Math.min(Math.round(huesettings.saturation * 254 / 100), 254) - } else if (huesettings.hex != null && false) { + } else if (huesettings.hex != null) { // For now ignore model to get a consistent color if same color is set across multiple devices // def model = state.bulbs[getId(childDevice)]?.modelid // value.xy = calculateXY(huesettings.hex, model) @@ -1663,7 +1663,7 @@ private boolean checkPointInLampsReach(p, colorPoints) { } /** - * Converts an RGB color in hex to HSV. + * Converts an RGB color in hex to HSV/HSB. * Algorithm based on http://en.wikipedia.org/wiki/HSV_color_space. * * @param colorStr color value in hex (#ff03d3) @@ -1673,32 +1673,32 @@ private boolean checkPointInLampsReach(p, colorPoints) { def hexToHsv(colorStr){ def r = Integer.valueOf( colorStr.substring( 1, 3 ), 16 ) / 255 def g = Integer.valueOf( colorStr.substring( 3, 5 ), 16 ) / 255 - def b = Integer.valueOf( colorStr.substring( 5, 7 ), 16 ) / 255; + def b = Integer.valueOf( colorStr.substring( 5, 7 ), 16 ) / 255 def max = Math.max(Math.max(r, g), b) def min = Math.min(Math.min(r, g), b) - def h, s, v = max; + def h, s, v = max - def d = max - min; - s = max == 0 ? 0 : d / max; + def d = max - min + s = max == 0 ? 0 : d / max if(max == min){ - h = 0; + h = 0 }else{ switch(max){ - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; + case r: h = (g - b) / d + (g < b ? 6 : 0); break + case g: h = (b - r) / d + 2; break + case b: h = (r - g) / d + 4; break } h /= 6; } - return [(h * 100).round(), (s * 100).round(), (v * 100).round()]; + return [Math.round(h * 100), Math.round(s * 100), Math.round(v * 100)] } /** - * Converts HSV color to RGB in hex. + * Converts HSV/HSB color to RGB in hex. * Algorithm based on http://en.wikipedia.org/wiki/HSV_color_space. * * @param hue hue 0-100 @@ -1713,11 +1713,11 @@ def hsvToHex(hue, sat, value = 100){ def s = sat / 100 def v = value / 100 - def i = Math.floor(h * 6); - def f = h * 6 - i; - def p = v * (1 - s); - def q = v * (1 - f * s); - def t = v * (1 - (1 - f) * s); + def i = Math.floor(h * 6) + def f = h * 6 - i + def p = v * (1 - s) + def q = v * (1 - f * s) + def t = v * (1 - (1 - f) * s) switch (i % 6) { case 0: @@ -1753,9 +1753,9 @@ def hsvToHex(hue, sat, value = 100){ } // Converting float components to int components. - def r1 = String.format("%02X", (int) (r * 255.0f)); - def g1 = String.format("%02X", (int) (g * 255.0f)); - def b1 = String.format("%02X", (int) (b * 255.0f)); + def r1 = String.format("%02X", (int) (r * 255.0f)) + def g1 = String.format("%02X", (int) (g * 255.0f)) + def b1 = String.format("%02X", (int) (b * 255.0f)) return "#$r1$g1$b1" } From 6400d26f4a9739edb97c7d7e00852e7b06dae32e Mon Sep 17 00:00:00 2001 From: Lars Finander Date: Fri, 23 Sep 2016 13:16:29 -0600 Subject: [PATCH 4/5] DVCSMP-2070 Philips Hue: No commands sent if light is unreachable -PROB-1384 --- .../hue-connect.src/hue-connect.groovy | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index 033f53c..e794b8c 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -950,6 +950,14 @@ private handleCommandResponse(body) { * @return empty array */ private handlePoll(body) { + // Used to track "unreachable" time + // Device is considered "offline" if it has been in the "unreachable" state for + // 11 minutes (e.g. two poll intervals) + // Note, Hue Bridge marks devices as "unreachable" often even when they accept commands + Calendar time11 = Calendar.getInstance() + time11.add(Calendar.MINUTE, -11) + Calendar currentTime = Calendar.getInstance() + def bulbs = getChildDevices() for (bulb in body) { def device = bulbs.find{it.deviceNetworkId == "${app.id}/${bulb.key}"} @@ -959,7 +967,10 @@ private handlePoll(body) { // 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) + 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 @@ -969,9 +980,18 @@ private handlePoll(body) { sendColorEvents(device, bulb.value?.state?.xy, bulb.value?.state?.hue, bulb.value?.state?.sat, bulb.value?.state?.ct, bulb.value?.state?.colormode) } } else { - state.bulbs[bulb.key]?.online = false - log.warn "$device is not reachable by Hue bridge" - device.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", displayed: false, isStateChange: true) + 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) { + // Check if device was "unreachable" for more than 11 minutes and mark "offline" if necessary + if (state.bulbs[bulb.key]?.unreachableSince < time11.getTimeInMillis()) { + log.warn "$device went Offline" + state.bulbs[bulb.key]?.online = false + device.sendEvent(name: "DeviceWatch-DeviceOffline", value: "offline", displayed: false, isStateChange: true) + } + } + log.warn "$device may not reachable by Hue bridge" } } } @@ -1006,9 +1026,6 @@ def hubVerification(bodytext) { def on(childDevice) { log.debug "Executing 'on'" def id = getId(childDevice) - if (!isOnline(id)) { - return "Bulb is unreachable" - } updateInProgress() createSwitchEvent(childDevice, "on") put("lights/$id/state", [on: true]) @@ -1018,9 +1035,6 @@ def on(childDevice) { def off(childDevice) { log.debug "Executing 'off'" def id = getId(childDevice) - if (!isOnline(id)) { - return "Bulb is unreachable" - } updateInProgress() createSwitchEvent(childDevice, "off") put("lights/$id/state", [on: false]) @@ -1030,9 +1044,6 @@ def off(childDevice) { def setLevel(childDevice, percent) { log.debug "Executing 'setLevel'" def id = getId(childDevice) - if (!isOnline(id)) { - return "Bulb is unreachable" - } updateInProgress() // 1 - 254 def level @@ -1057,10 +1068,6 @@ def setLevel(childDevice, percent) { def setSaturation(childDevice, percent) { log.debug "Executing 'setSaturation($percent)'" def id = getId(childDevice) - if (!isOnline(id)) { - return "Bulb is unreachable" - } - updateInProgress() // 0 - 254 def level = Math.min(Math.round(percent * 254 / 100), 254) @@ -1073,9 +1080,6 @@ def setSaturation(childDevice, percent) { def setHue(childDevice, percent) { log.debug "Executing 'setHue($percent)'" def id = getId(childDevice) - if (!isOnline(id)) { - return "Bulb is unreachable" - } updateInProgress() // 0 - 65535 def level = Math.min(Math.round(percent * 65535 / 100), 65535) @@ -1088,9 +1092,6 @@ def setHue(childDevice, percent) { def setColorTemperature(childDevice, huesettings) { log.debug "Executing 'setColorTemperature($huesettings)'" def id = getId(childDevice) - if (!isOnline(id)) { - return "Bulb is unreachable" - } updateInProgress() // 153 (6500K) to 500 (2000K) def ct = hueSettings == 6500 ? 153 : Math.round(1000000/huesettings) @@ -1102,9 +1103,6 @@ def setColorTemperature(childDevice, huesettings) { def setColor(childDevice, huesettings) { log.debug "Executing 'setColor($huesettings)'" def id = getId(childDevice) - if (!isOnline(id)) { - return "Bulb is unreachable" - } updateInProgress() def value = [:] @@ -1224,7 +1222,7 @@ private getBridgeIP() { if (d) { if (d.getDeviceDataByName("networkAddress")) host = d.getDeviceDataByName("networkAddress") - else + else host = d.latestState('networkAddress').stringValue } if (host == null || host == "") { From a0ccf35eaa07224fa6c36c6260a4bba541dc6cdc Mon Sep 17 00:00:00 2001 From: Vinay Rao Date: Mon, 26 Sep 2016 14:17:36 -0700 Subject: [PATCH 5/5] SSVD-2897 to round celsius and fix rounding on fahrenheit --- .../smartsense-moisture-sensor.groovy | 4 ++-- .../smartsense-motion-sensor.groovy | 4 ++-- .../smartsense-multi-sensor.groovy | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy index 1cce229..d1b4623 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy @@ -180,9 +180,9 @@ private Map parseIasMessage(String description) { def getTemperature(value) { def celsius = Integer.parseInt(value, 16).shortValue() / 100 if(getTemperatureScale() == "C"){ - return celsius + return Math.round(celsius) } else { - return celsiusToFahrenheit(celsius) as Integer + return Math.round(celsiusToFahrenheit(celsius)) } } diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index d8f3e7a..3a8cbde 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -194,9 +194,9 @@ private Map parseIasMessage(String description) { def getTemperature(value) { def celsius = Integer.parseInt(value, 16).shortValue() / 100 if(getTemperatureScale() == "C"){ - return celsius + return Math.round(celsius) } else { - return celsiusToFahrenheit(celsius) as Integer + return Math.round(celsiusToFahrenheit(celsius)) } } diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index 36e081f..25c6591 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -261,9 +261,9 @@ def updated() { def getTemperature(value) { def celsius = Integer.parseInt(value, 16).shortValue() / 100 if(getTemperatureScale() == "C"){ - return celsius + return Math.round(celsius) } else { - return celsiusToFahrenheit(celsius) as Integer + return Math.round(celsiusToFahrenheit(celsius)) } }