From 6bda59c340748dc23ec9184aa8c22b14629e183f Mon Sep 17 00:00:00 2001 From: juano2310 Date: Fri, 4 Mar 2016 17:08:24 -0500 Subject: [PATCH 1/3] DVCSMP-1565 & DVCSMP-1548 DVCSMP-1565 Color for light is not adjusted. DVCSMP-1548 Color temperature --- .../smartthings/hue-bulb.src/hue-bulb.groovy | 79 ++++++++++++++---- .../hue-lux-bulb.src/hue-lux-bulb.groovy | 66 +++++++++------ .../hue-connect.src/hue-connect.groovy | 82 ++++++++++--------- 3 files changed, 147 insertions(+), 80 deletions(-) diff --git a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy index 20a12ec..f536ef4 100644 --- a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy +++ b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy @@ -1,4 +1,3 @@ - /** * Hue Bulb * @@ -11,13 +10,14 @@ metadata { capability "Switch Level" capability "Actuator" capability "Color Control" + capability "Color Temperature" capability "Switch" capability "Refresh" capability "Sensor" command "setAdjustedColor" - command "reset" - command "refresh" + command "reset" + command "refresh" } simulator { @@ -25,7 +25,7 @@ metadata { } tiles (scale: 2){ - multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ + multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" @@ -33,23 +33,58 @@ metadata { attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" } tileAttribute ("device.level", key: "SLIDER_CONTROL") { - attributeState "level", action:"switch level.setLevel" + attributeState "level", action:"switch level.setLevel", range:"(0..100)" + } + tileAttribute ("device.level", key: "SECONDARY_CONTROL") { + attributeState "level", label: 'Level ${currentValue}%' } tileAttribute ("device.color", key: "COLOR_CONTROL") { attributeState "color", action:"setAdjustedColor" } } - standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { + state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" + state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" + state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + } + + controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") { + state "colorTemperature", action:"color temperature.setColorTemperature" + } + valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "colorTemperature", label: '${currentValue} K' + } + + standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single" } - standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + standardTile("refresh", "device.switch", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } - } + controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { + state "level", action:"switch level.setLevel" + } + valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") { + state "level", label: 'Level ${currentValue}%' + } + controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) { + state "saturation", action:"color control.setSaturation" + } + valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") { + state "saturation", label: 'Sat ${currentValue} ' + } + controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) { + state "hue", action:"color control.setHue" + } + valueTile("hue", "device.hue", inactiveLabel: false, decoration: "flat") { + state "hue", label: 'Hue ${currentValue} ' + } - main(["switch"]) - details(["switch", "levelSliderControl", "rgbSelector", "refresh", "reset"]) + main(["switch"]) + details(["rich-control", "levelSliderControl", "rgbSelector", "colorTempSliderControl", "colorTemp", "reset", "refresh"]) + } } // parse events into attributes @@ -119,19 +154,27 @@ void setColor(value) { void reset() { log.debug "Executing 'reset'" - def value = [level:100, hex:"#90C638", saturation:56, hue:23] - setAdjustedColor(value) + def value = [level:100, hex:"#90C638", saturation:56, hue:23] + setAdjustedColor(value) parent.poll() } void setAdjustedColor(value) { if (value) { - log.trace "setAdjustedColor: ${value}" - def adjusted = value + [:] - adjusted.hue = adjustOutgoingHue(value.hue) - // Needed because color picker always sends 100 - adjusted.level = null - setColor(adjusted) + log.trace "setAdjustedColor: ${value}" + def adjusted = value + [:] + adjusted.hue = adjustOutgoingHue(value.hue) + // Needed because color picker always sends 100 + adjusted.level = null + setColor(adjusted) + } +} + +void setColorTemperature(value) { + if (value) { + log.trace "setColorTemperature: ${value}k" + parent.setColorTemperature(this, value) + sendEvent(name: "colorTemperature", value: value) } } 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 ae0a5ba..81261e8 100644 --- a/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy +++ b/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy @@ -9,51 +9,59 @@ metadata { definition (name: "Hue Lux Bulb", namespace: "smartthings", author: "SmartThings") { capability "Switch Level" capability "Actuator" + capability "Color Temperature" capability "Switch" capability "Refresh" capability "Sensor" - command "refresh" + command "refresh" } simulator { // TODO: define status and reply messages here } - tiles(scale: 2) { - multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){ - tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { - attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" - attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" - attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" - attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + tiles(scale: 2) { + multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){ + tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { + attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" + attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" + attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + } + tileAttribute ("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action:"switch level.setLevel", range:"(0..100)" + } + tileAttribute ("device.level", key: "SECONDARY_CONTROL") { + attributeState "level", label: 'Level ${currentValue}%' } - tileAttribute ("device.level", key: "SLIDER_CONTROL") { - attributeState "level", action:"switch level.setLevel", range:"(0..100)" - } - tileAttribute ("device.level", key: "SECONDARY_CONTROL") { - attributeState "level", label: 'Level ${currentValue}%' - } - } + } standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" - } + } - controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { - state "level", action:"switch level.setLevel" - } + controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { + state "level", action:"switch level.setLevel" + } - standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") { - state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" - } + controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") { + state "colorTemperature", action:"color temperature.setColorTemperature" + } + valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "colorTemperature", label: '${currentValue} K' + } - main(["switch"]) - details(["rich-control", "refresh"]) - } + standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } + + main(["switch"]) + details(["rich-control", "colorTempSliderControl", "colorTemp", "refresh"]) + } } // parse events into attributes @@ -90,6 +98,14 @@ void setLevel(percent) { sendEvent(name: "level", value: percent) } +void setColorTemperature(value) { + if (value) { + log.trace "setColorTemperature: ${value}k" + parent.setColorTemperature(this, value) + sendEvent(name: "colorTemperature", value: value) + } +} + void refresh() { log.debug "Executing 'refresh'" parent.manualRefresh() diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index 048390f..b68db82 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -15,7 +15,7 @@ * for the specific language governing permissions and limitations under the License. * */ - + definition( name: "Hue (Connect)", namespace: "smartthings", @@ -58,7 +58,7 @@ def bridgeDiscovery(params=[:]) state.bridges = [:] state.bridgeRefreshCount = 0 app.updateSetting("selectedHue", "") - } + } subscribe(location, null, locationHandler, [filterEvents:false]) @@ -130,8 +130,8 @@ def bulbDiscovery() { def bulboptions = bulbsDiscovered() ?: [:] def numFound = bulboptions.size() ?: 0 if (numFound == 0) - app.updateSetting("selectedBulbs", "") - + app.updateSetting("selectedBulbs", "") + if((bulbRefreshCount % 5) == 0) { discoverHueBulbs() } @@ -140,7 +140,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.") { input "selectedBulbs", "enum", required:false, title:"Select Hue Bulbs (${numFound} found)", multiple:true, options:bulboptions } - section { + section { def title = getBridgeIP() ? "Hue bridge (${getBridgeIP()})" : "Find bridges" href "bridgeDiscovery", title: title, description: "", state: selectedHue ? "complete" : "incomplete", params: [override: true] @@ -246,13 +246,13 @@ def installed() { def updated() { log.trace "Updated with settings: ${settings}" - unsubscribe() - unschedule() + unsubscribe() + unschedule() initialize() } def initialize() { - log.debug "Initializing" + log.debug "Initializing" unsubscribe(bridge) state.inBulbDiscovery = false state.bridgeRefreshCount = 0 @@ -281,18 +281,18 @@ def uninstalled(){ def bulbListHandler(hub, data = "") { def msg = "Bulbs list not processed. Only while in settings menu." def bulbs = [:] - if (state.inBulbDiscovery) { + if (state.inBulbDiscovery) { def logg = "" log.trace "Adding bulbs to state..." state.bridgeProcessedLightList = true - def object = new groovy.json.JsonSlurper().parseText(data) + def object = new groovy.json.JsonSlurper().parseText(data) object.each { k,v -> - if (v instanceof Map) + if (v instanceof Map) bulbs[k] = [id: k, name: v.name, type: v.type, hub:hub] } - } + } def bridge = null - if (selectedHue) + if (selectedHue) bridge = getChildDevice(selectedHue) bridge.sendEvent(name: "bulbList", value: hub, data: bulbs, isStateChange: true, displayed: false) msg = "${bulbs.size()} bulbs found. ${bulbs}" @@ -318,7 +318,7 @@ def addBulbs() { } else { log.debug "$dni in not longer paired to the Hue Bridge or ID changed" } - } else { + } else { //backwards compatable newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni } d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name]) @@ -344,7 +344,7 @@ def addBridge() { def d = getChildDevice(selectedHue) if(!d) { // compatibility with old devices - def newbridge = true + def newbridge = true childDevices.each { if (it.getDeviceDataByName("mac")) { def newDNI = "${it.getDeviceDataByName("mac")}" @@ -354,10 +354,10 @@ def addBridge() { it.setDeviceNetworkId("${newDNI}") if (oldDNI == selectedHue) app.updateSetting("selectedHue", newDNI) - newbridge = false + newbridge = false } - } - } + } + } if (newbridge) { d = addChildDevice("smartthings", "Hue Bridge", selectedHue, vbridge.value.hub) log.debug "created ${d.displayName} with id ${d.deviceNetworkId}" @@ -368,13 +368,13 @@ def addBridge() { childDevice.sendEvent(name: "networkAddress", value: vbridge.value.ip + ":" + vbridge.value.port) childDevice.updateDataValue("networkAddress", vbridge.value.ip + ":" + vbridge.value.port) } else { - childDevice.sendEvent(name: "networkAddress", value: convertHexToIP(vbridge.value.ip) + ":" + convertHexToInt(vbridge.value.port)) + 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.updateDataValue("networkAddress", convertHexToIP(vbridge.value.networkAddress) + ":" + convertHexToInt(vbridge.value.deviceAddress)) - } + } } } else { log.debug "found ${d.displayName} with id $selectedHue already exists" @@ -436,7 +436,7 @@ def locationHandler(evt) { dstate.name = "Philips hue ($ip)" d.sendEvent(name:"networkAddress", value: host) d.updateDataValue("networkAddress", host) - } + } } } } @@ -504,11 +504,11 @@ def isValidSource(macAddress) { ///////////////////////////////////// def parse(childDevice, description) { - def parsedEvent = parseLanMessage(description) + def parsedEvent = parseLanMessage(description) if (parsedEvent.headers && parsedEvent.body) { def headerString = parsedEvent.headers.toString() def bodyString = parsedEvent.body.toString() - if (headerString?.contains("json")) { + if (headerString?.contains("json")) { def body try { body = new groovy.json.JsonSlurper().parseText(bodyString) @@ -516,11 +516,11 @@ def parse(childDevice, description) { log.warn "Parsing Body failed - trying again..." poll() } - if (body instanceof java.util.HashMap) { + if (body instanceof java.util.HashMap) { //poll response def bulbs = getChildDevices() 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 (bulb.value.state?.reachable) { sendEvent(d.deviceNetworkId, [name: "switch", value: bulb.value?.state?.on ? "on" : "off"]) @@ -535,18 +535,18 @@ def parse(childDevice, description) { } } else { 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) { def hue = 23 def sat = 56 def hex = colorUtil.hslToHex(23, 56) sendEvent(d.deviceNetworkId, [name: "color", value: hex]) sendEvent(d.deviceNetworkId, [name: "hue", value: hue]) - sendEvent(d.deviceNetworkId, [name: "saturation", value: sat]) - } + sendEvent(d.deviceNetworkId, [name: "saturation", value: sat]) + } } } - } + } } else { //put response @@ -595,7 +595,7 @@ def parse(childDevice, description) { } } - } + } } else { log.debug "parse - got something other than headers,body..." return [] @@ -616,7 +616,7 @@ def off(childDevice) { def setLevel(childDevice, percent) { log.debug "Executing 'setLevel'" - def level + def level if (percent == 1) level = 1 else level = Math.min(Math.round(percent * 255 / 100), 255) put("lights/${getId(childDevice)}/state", [bri: level, on: percent > 0]) } @@ -633,6 +633,14 @@ def setHue(childDevice, percent) { put("lights/${getId(childDevice)}/state", [hue: level]) } +def setColorTemperature(childDevice, huesettings) { + log.debug "Executing 'setColorTemperature($huesettings)'" + def ct = Math.round(Math.abs((huesettings / 12.96829971181556) - 654)) + def value = [ct: ct, on: true] + log.trace "sending command $value" + put("lights/${getId(childDevice)}/state", value) +} + def setColor(childDevice, huesettings) { log.debug "Executing 'setColor($huesettings)'" def hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535) @@ -689,7 +697,7 @@ HOST: ${host} } private put(path, body) { - def host = getBridgeIP() + def host = getBridgeIP() def uri = "/api/${state.username}/$path" def bodyJSON = new groovy.json.JsonBuilder(body).toString() def length = bodyJSON.getBytes().size().toString() @@ -715,11 +723,11 @@ private getBridgeIP() { 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) { + if (!bridge) { bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(serialNumber) }?.value } if (bridge?.ip && bridge?.port) { @@ -729,9 +737,9 @@ private getBridgeIP() { 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 } From fb99a81704a34b8ba70b751f3f3e6fe2281b9fff Mon Sep 17 00:00:00 2001 From: juano2310 Date: Fri, 4 Mar 2016 17:15:49 -0500 Subject: [PATCH 2/3] fixed rich-control --- .../smartthings/hue-bulb.src/hue-bulb.groovy | 147 ++++-------------- 1 file changed, 29 insertions(+), 118 deletions(-) diff --git a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy index f536ef4..47f2971 100644 --- a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy +++ b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy @@ -1,54 +1,52 @@ /** - * Hue Bulb + * Hue Lux Bulb * * Author: SmartThings */ // for the UI metadata { // Automatically generated. Make future change here. - definition (name: "Hue Bulb", namespace: "smartthings", author: "SmartThings") { + definition (name: "Hue Lux Bulb", namespace: "smartthings", author: "SmartThings") { capability "Switch Level" capability "Actuator" - capability "Color Control" capability "Color Temperature" capability "Switch" capability "Refresh" capability "Sensor" - command "setAdjustedColor" - command "reset" - command "refresh" + command "refresh" } simulator { // TODO: define status and reply messages here } - tiles (scale: 2){ - multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ - tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { - attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" - attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" - attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" - attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" - } - tileAttribute ("device.level", key: "SLIDER_CONTROL") { - attributeState "level", action:"switch level.setLevel", range:"(0..100)" + tiles(scale: 2) { + multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){ + tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { + attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" + attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" + attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + } + tileAttribute ("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action:"switch level.setLevel", range:"(0..100)" } tileAttribute ("device.level", key: "SECONDARY_CONTROL") { attributeState "level", label: 'Level ${currentValue}%' } - tileAttribute ("device.color", key: "COLOR_CONTROL") { - attributeState "color", action:"setAdjustedColor" - } - } + } standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" - } + } + + controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { + state "level", action:"switch level.setLevel" + } controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") { state "colorTemperature", action:"color temperature.setColorTemperature" @@ -57,45 +55,26 @@ metadata { state "colorTemperature", label: '${currentValue} K' } - standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { - state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single" - } - standardTile("refresh", "device.switch", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { - state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" - } - controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { - state "level", action:"switch level.setLevel" - } - valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") { - state "level", label: 'Level ${currentValue}%' - } - controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) { - state "saturation", action:"color control.setSaturation" - } - valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") { - state "saturation", label: 'Sat ${currentValue} ' - } - controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) { - state "hue", action:"color control.setHue" - } - valueTile("hue", "device.hue", inactiveLabel: false, decoration: "flat") { - state "hue", label: 'Hue ${currentValue} ' - } + standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } - main(["switch"]) - details(["rich-control", "levelSliderControl", "rgbSelector", "colorTempSliderControl", "colorTemp", "reset", "refresh"]) - } + main(["switch"]) + details(["rich-control", "colorTempSliderControl", "colorTemp", "refresh"]) + } } // parse events into attributes def parse(description) { log.debug "parse() - $description" def results = [] + def map = description if (description instanceof String) { log.debug "Hue Bulb stringToMap - ${map}" map = stringToMap(description) } + if (map?.name && map?.value) { results << createEvent(name: "${map?.name}", value: "${map?.value}") } @@ -104,72 +83,21 @@ def parse(description) { // handle commands void on() { - log.trace parent.on(this) + parent.on(this) sendEvent(name: "switch", value: "on") } void off() { - log.trace parent.off(this) + parent.off(this) sendEvent(name: "switch", value: "off") } -void nextLevel() { - def level = device.latestValue("level") as Integer ?: 0 - if (level <= 100) { - level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer - } - else { - level = 25 - } - setLevel(level) -} - void setLevel(percent) { log.debug "Executing 'setLevel'" parent.setLevel(this, percent) sendEvent(name: "level", value: percent) } -void setSaturation(percent) { - log.debug "Executing 'setSaturation'" - parent.setSaturation(this, percent) - sendEvent(name: "saturation", value: percent) -} - -void setHue(percent) { - log.debug "Executing 'setHue'" - parent.setHue(this, percent) - sendEvent(name: "hue", value: percent) -} - -void setColor(value) { - log.debug "setColor: ${value}, $this" - parent.setColor(this, value) - if (value.hue) { sendEvent(name: "hue", value: value.hue)} - if (value.saturation) { sendEvent(name: "saturation", value: value.saturation)} - if (value.hex) { sendEvent(name: "color", value: value.hex)} - if (value.level) { sendEvent(name: "level", value: value.level)} - if (value.switch) { sendEvent(name: "switch", value: value.switch)} -} - -void reset() { - log.debug "Executing 'reset'" - def value = [level:100, hex:"#90C638", saturation:56, hue:23] - setAdjustedColor(value) - parent.poll() -} - -void setAdjustedColor(value) { - if (value) { - log.trace "setAdjustedColor: ${value}" - def adjusted = value + [:] - adjusted.hue = adjustOutgoingHue(value.hue) - // Needed because color picker always sends 100 - adjusted.level = null - setColor(adjusted) - } -} - void setColorTemperature(value) { if (value) { log.trace "setColorTemperature: ${value}k" @@ -182,20 +110,3 @@ void refresh() { log.debug "Executing 'refresh'" parent.manualRefresh() } - -def adjustOutgoingHue(percent) { - def adjusted = percent - if (percent > 31) { - if (percent < 63.0) { - adjusted = percent + (7 * (percent -30 ) / 32) - } - else if (percent < 73.0) { - adjusted = 69 + (5 * (percent - 62) / 10) - } - else { - adjusted = percent + (2 * (100 - percent) / 28) - } - } - log.info "percent: $percent, adjusted: $adjusted" - adjusted -} From 9880ced8517e330dfb0f0cd105f04ea24d23d0f2 Mon Sep 17 00:00:00 2001 From: juano2310 Date: Mon, 7 Mar 2016 17:05:06 -0800 Subject: [PATCH 3/3] Fix wrong DT for Hue bulb --- .../smartthings/hue-bulb.src/hue-bulb.groovy | 147 ++++++++++++++---- 1 file changed, 118 insertions(+), 29 deletions(-) diff --git a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy index 47f2971..f49ec95 100644 --- a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy +++ b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy @@ -1,52 +1,54 @@ /** - * Hue Lux Bulb + * Hue Bulb * * Author: SmartThings */ // for the UI metadata { // Automatically generated. Make future change here. - definition (name: "Hue Lux Bulb", namespace: "smartthings", author: "SmartThings") { + definition (name: "Hue Bulb", namespace: "smartthings", author: "SmartThings") { capability "Switch Level" capability "Actuator" + capability "Color Control" capability "Color Temperature" capability "Switch" capability "Refresh" capability "Sensor" - command "refresh" + command "setAdjustedColor" + command "reset" + command "refresh" } simulator { // TODO: define status and reply messages here } - tiles(scale: 2) { - multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){ - tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { - attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" - attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" - attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" - attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" - } - tileAttribute ("device.level", key: "SLIDER_CONTROL") { - attributeState "level", action:"switch level.setLevel", range:"(0..100)" + tiles (scale: 2){ + multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){ + tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { + attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" + attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" + attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" + } + tileAttribute ("device.level", key: "SLIDER_CONTROL") { + attributeState "level", action:"switch level.setLevel", range:"(0..100)" } tileAttribute ("device.level", key: "SECONDARY_CONTROL") { attributeState "level", label: 'Level ${currentValue}%' } - } + tileAttribute ("device.color", key: "COLOR_CONTROL") { + attributeState "color", action:"setAdjustedColor" + } + } standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff" state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" - } - - controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { - state "level", action:"switch level.setLevel" - } + } controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") { state "colorTemperature", action:"color temperature.setColorTemperature" @@ -55,26 +57,45 @@ metadata { state "colorTemperature", label: '${currentValue} K' } - standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { - state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" - } + standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single" + } + standardTile("refresh", "device.switch", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { + state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" + } + controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { + state "level", action:"switch level.setLevel" + } + valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") { + state "level", label: 'Level ${currentValue}%' + } + controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) { + state "saturation", action:"color control.setSaturation" + } + valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") { + state "saturation", label: 'Sat ${currentValue} ' + } + controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) { + state "hue", action:"color control.setHue" + } + valueTile("hue", "device.hue", inactiveLabel: false, decoration: "flat") { + state "hue", label: 'Hue ${currentValue} ' + } - main(["switch"]) - details(["rich-control", "colorTempSliderControl", "colorTemp", "refresh"]) - } + main(["switch"]) + details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"]) + } } // parse events into attributes def parse(description) { log.debug "parse() - $description" def results = [] - def map = description if (description instanceof String) { log.debug "Hue Bulb stringToMap - ${map}" map = stringToMap(description) } - if (map?.name && map?.value) { results << createEvent(name: "${map?.name}", value: "${map?.value}") } @@ -83,21 +104,72 @@ def parse(description) { // handle commands void on() { - parent.on(this) + log.trace parent.on(this) sendEvent(name: "switch", value: "on") } void off() { - parent.off(this) + log.trace parent.off(this) sendEvent(name: "switch", value: "off") } +void nextLevel() { + def level = device.latestValue("level") as Integer ?: 0 + if (level <= 100) { + level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer + } + else { + level = 25 + } + setLevel(level) +} + void setLevel(percent) { log.debug "Executing 'setLevel'" parent.setLevel(this, percent) sendEvent(name: "level", value: percent) } +void setSaturation(percent) { + log.debug "Executing 'setSaturation'" + parent.setSaturation(this, percent) + sendEvent(name: "saturation", value: percent) +} + +void setHue(percent) { + log.debug "Executing 'setHue'" + parent.setHue(this, percent) + sendEvent(name: "hue", value: percent) +} + +void setColor(value) { + log.debug "setColor: ${value}, $this" + parent.setColor(this, value) + if (value.hue) { sendEvent(name: "hue", value: value.hue)} + if (value.saturation) { sendEvent(name: "saturation", value: value.saturation)} + if (value.hex) { sendEvent(name: "color", value: value.hex)} + if (value.level) { sendEvent(name: "level", value: value.level)} + if (value.switch) { sendEvent(name: "switch", value: value.switch)} +} + +void reset() { + log.debug "Executing 'reset'" + def value = [level:100, hex:"#90C638", saturation:56, hue:23] + setAdjustedColor(value) + parent.poll() +} + +void setAdjustedColor(value) { + if (value) { + log.trace "setAdjustedColor: ${value}" + def adjusted = value + [:] + adjusted.hue = adjustOutgoingHue(value.hue) + // Needed because color picker always sends 100 + adjusted.level = null + setColor(adjusted) + } +} + void setColorTemperature(value) { if (value) { log.trace "setColorTemperature: ${value}k" @@ -110,3 +182,20 @@ void refresh() { log.debug "Executing 'refresh'" parent.manualRefresh() } + +def adjustOutgoingHue(percent) { + def adjusted = percent + if (percent > 31) { + if (percent < 63.0) { + adjusted = percent + (7 * (percent -30 ) / 32) + } + else if (percent < 73.0) { + adjusted = 69 + (5 * (percent - 62) / 10) + } + else { + adjusted = percent + (2 * (100 - percent) / 28) + } + } + log.info "percent: $percent, adjusted: $adjusted" + adjusted +}