Compare commits

..

1 Commits

2 changed files with 194 additions and 264 deletions

View File

@@ -1,264 +0,0 @@
/*
Commercial Electric Smart LED Recessed Downlight (Model # 53166161)
Modified Osram device handler for Commercial Electric Smart LED Recessed Downlight available at Home Depot (Model # 53166161).
This is a Home Depot house brand manufactured by ETI Solid State Lighting, a subsidiary of Elec-Tech International, HK
Only modification required was the range of Color Temperature - warm white (2700K) to daylight (5000K).
*/
metadata {
definition (name: "Commercial Electric Smart LED Recessed Downlight", namespace: "ericcreighton", author: "Eric Creighton") {
capability "Color Temperature"
capability "Actuator"
capability "Switch"
capability "Switch Level"
capability "Configuration"
capability "Refresh"
capability "Sensor"
attribute "colorName", "string"
// indicates that device keeps track of heartbeat (in state.heartbeat)
attribute "heartbeat", "string"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "ETI", model: "531661XX"
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
// UI tile definitions
tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..5000)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat") {
state "colorTemperature", label: '${currentValue} K'
}
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat") {
state "colorName", label: '${currentValue}'
}
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}%'
}
main(["switch"])
details(["switch", "refresh", "colorName", "levelSliderControl", "level", "colorTempSliderControl", "colorTemp"])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
//log.trace description
// save heartbeat (i.e. last time we got a message from device)
state.heartbeat = Calendar.getInstance().getTimeInMillis()
if (description?.startsWith("catchall:")) {
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
{
def result = createEvent(name: "switch", value: "on")
log.debug "Parse returned ${result?.descriptionText}"
return result
}
else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
{
def result = createEvent(name: "switch", value: "off")
log.debug "Parse returned ${result?.descriptionText}"
return result
}
}
else if (description?.startsWith("read attr -")) {
def descMap = parseDescriptionAsMap(description)
log.trace "descMap : $descMap"
if (descMap.cluster == "0300") {
log.debug descMap.value
def tempInMired = convertHexToInt(descMap.value)
def tempInKelvin = Math.round(1000000/tempInMired)
log.trace "temp in kelvin: $tempInKelvin"
sendEvent(name: "colorTemperature", value: tempInKelvin, displayed:false)
}
else if(descMap.cluster == "0008"){
def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
log.debug "dimmer value is $dimmerValue"
sendEvent(name: "level", value: dimmerValue)
}
}
else {
def name = description?.startsWith("on/off: ") ? "switch" : null
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
def result = createEvent(name: name, value: value)
log.debug "Parse returned ${result?.descriptionText}"
return result
}
}
def on() {
log.debug "on()"
sendEvent(name: "switch", value: "on")
setLevel(state?.levelValue)
}
def off() {
log.debug "off()"
sendEvent(name: "switch", value: "off")
"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
}
def refresh() {
sendEvent(name: "heartbeat", value: "alive", displayed:false)
[
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 7"
]
}
def configure() {
state.levelValue = 100
log.debug "Configuring Reporting and Bindings."
def configCmds = [
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
]
return onOffConfig() + levelConfig() + configCmds + refresh() // send refresh cmds as part of config
}
def onOffConfig() {
[
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 6 0 0x10 0 300 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
//level config for devices with min reporting interval as 5 seconds and reporting interval if no activity as 1hour (3600s)
//min level change is 01
def levelConfig() {
[
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 8 0 0x20 5 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
def setColorTemperature(value) {
if(value<101){
value = (value*38) + 2700 //Calculation of mapping 0-100 to 2700-6500
}
def tempInMired = Math.round(1000000/value)
def finalHex = swapEndianHex(hex(tempInMired, 4))
def genericName = getGenericName(value)
log.debug "generic name is : $genericName"
def cmds = []
sendEvent(name: "colorTemperature", value: value, displayed:false)
sendEvent(name: "colorName", value: genericName)
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x0300 0x0a {${finalHex} 2000}"
cmds
}
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
def setLevel(value) {
state.levelValue = (value==null) ? 100 : value
log.trace "setLevel($value)"
def cmds = []
if (value == 0) {
sendEvent(name: "switch", value: "off")
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
}
else if (device.latestValue("switch") == "off") {
sendEvent(name: "switch", value: "on")
}
sendEvent(name: "level", value: state.levelValue)
def level = hex(state.levelValue * 254 / 100)
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}"
//log.debug cmds
cmds
}
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
private getGenericName(value){
def genericName = "White"
if(value < 3300){
genericName = "Soft White"
} else if(value < 4150){
genericName = "Moonlight"
} else if(value < 5000){
genericName = "Cool White"
} else if(value <= 6500){
genericName = "Daylight"
}
genericName
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
//Need to reverse array of size 2
private byte[] reverseArray(byte[] array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}

View File

@@ -0,0 +1,194 @@
/**
* Hello Home Cube
*
* Copyright 2015 skp19
*
*
*/
/************
* Metadata *
************/
definition(
name: "Hello Home Cube",
namespace: "skp19",
author: "skp19",
description: "Run a Hello Home action by rotating a cube containing a SmartSense Multi",
category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-LightUpMyWorld.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/App-LightUpMyWorld@2x.png"
)
import groovy.json.JsonSlurper
/**********
* Setup *
**********/
preferences {
page(name: "mainPage", title: "", nextPage: "scenesPage", uninstall: true) {
section("Use the orientation of this cube") {
input "cube", "capability.threeAxis", required: false, title: "SmartSense Multi sensor"
}
section([title: " ", mobileOnly:true]) {
label title: "Assign a name", required: false
mode title: "Set for specific mode(s)", required: false
}
}
page(name: "scenesPage", title: "Scenes", install: true, uninstall: true)
}
def scenesPage() {
log.debug "scenesPage()"
def sceneId = getOrientation()
dynamicPage(name:"scenesPage") {
def phrases = location.helloHome?.getPhrases()*.label
section {
phrases.sort()
input name: "homeAction1", type: "enum", title: "${1}. ${sceneName(1)}${sceneId==1 ? ' (current)' : ''}", required: false, options: phrases
input name: "homeAction2", type: "enum", title: "${2}. ${sceneName(2)}${sceneId==2 ? ' (current)' : ''}", required: false, options: phrases
input name: "homeAction3", type: "enum", title: "${3}. ${sceneName(3)}${sceneId==3 ? ' (current)' : ''}", required: false, options: phrases
input name: "homeAction4", type: "enum", title: "${4}. ${sceneName(4)}${sceneId==4 ? ' (current)' : ''}", required: false, options: phrases
input name: "homeAction5", type: "enum", title: "${5}. ${sceneName(5)}${sceneId==5 ? ' (current)' : ''}", required: false, options: phrases
input name: "homeAction6", type: "enum", title: "${6}. ${sceneName(6)}${sceneId==6 ? ' (current)' : ''}", required: false, options: phrases
}
section {
href "scenesPage", title: "Refresh", description: ""
}
}
}
/*************************
* Installation & update *
*************************/
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
subscribe cube, "threeAxis", positionHandler
}
/******************
* Event handlers *
******************/
def positionHandler(evt) {
def sceneId = getOrientation(evt.xyzValue)
log.trace "orientation: $sceneId"
if (sceneId != state.lastActiveSceneId) {
runHomeAction(sceneId)
}
else {
log.trace "No status change"
}
state.lastActiveSceneId = sceneId
}
/******************
* Helper methods *
******************/
private Boolean sceneIsDefined(sceneId) {
def tgt = "onoff_${sceneId}".toString()
settings.find{it.key.startsWith(tgt)} != null
}
private updateSetting(name, value) {
app.updateSetting(name, value)
settings[name] = value
}
private runHomeAction(sceneId) {
log.trace "runHomeAction($sceneId)"
//RUN HELLO HOME ACTION
def homeAction
if (sceneId == 1) {
homeAction = homeAction1
}
if (sceneId == 2) {
homeAction = homeAction2
}
if (sceneId == 3) {
homeAction = homeAction3
}
if (sceneId == 4) {
homeAction = homeAction4
}
if (sceneId == 5) {
homeAction = homeAction5
}
if (sceneId == 6) {
homeAction = homeAction6
}
if (homeAction) {
location.helloHome.execute(homeAction)
log.trace "Running Home Action: $homeAction"
}
else {
log.trace "No Home Action Defined for Current State"
}
}
private getOrientation(xyz=null) {
final threshold = 250
def value = xyz ?: cube.currentValue("threeAxis")
def x = Math.abs(value.x) > threshold ? (value.x > 0 ? 1 : -1) : 0
def y = Math.abs(value.y) > threshold ? (value.y > 0 ? 1 : -1) : 0
def z = Math.abs(value.z) > threshold ? (value.z > 0 ? 1 : -1) : 0
def orientation = 0
if (z > 0) {
if (x == 0 && y == 0) {
orientation = 1
}
}
else if (z < 0) {
if (x == 0 && y == 0) {
orientation = 2
}
}
else {
if (x > 0) {
if (y == 0) {
orientation = 3
}
}
else if (x < 0) {
if (y == 0) {
orientation = 4
}
}
else {
if (y > 0) {
orientation = 5
}
else if (y < 0) {
orientation = 6
}
}
}
orientation
}
private sceneName(num) {
final names = ["UNDEFINED","One","Two","Three","Four","Five","Six"]
settings."sceneName${num}" ?: "Scene ${names[num]}"
}